@open-mercato/core 0.6.7-develop.6862.1.c11a64ce0a → 0.6.7-develop.6869.1.d1ce9999ea
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/attachments/backend/config/attachments/page.js +1 -1
- package/dist/modules/attachments/backend/config/attachments/page.js.map +2 -2
- package/dist/modules/audit_logs/components/AuditLogsActions.js +1 -1
- package/dist/modules/audit_logs/components/AuditLogsActions.js.map +2 -2
- package/dist/modules/auth/frontend/login.js +2 -2
- package/dist/modules/auth/frontend/login.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js +10 -5
- package/dist/modules/catalog/widgets/injection/product-bulk-delete/widget.js.map +2 -2
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +4 -4
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
- package/dist/modules/customers/api/utils.js +9 -6
- package/dist/modules/customers/api/utils.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +1 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/MiniWeekCalendar.js +7 -52
- package/dist/modules/customers/components/detail/MiniWeekCalendar.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -1
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/legacyActivities.js +69 -0
- package/dist/modules/customers/components/detail/legacyActivities.js.map +7 -0
- package/dist/modules/customers/utils/phoneDuplicates.js +1 -2
- package/dist/modules/customers/utils/phoneDuplicates.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +3 -3
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +5 -5
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/entities/api/records.js +8 -4
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js +1 -1
- package/dist/modules/entities/backend/entities/user/[entityId]/page.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js +1 -1
- package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +1 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +1 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js +1 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/reset-password/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js +1 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/verify/page.js +1 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/verify/page.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +11 -1
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js +1 -1
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +1 -1
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +3 -3
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +3 -3
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +3 -3
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -1
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +1 -1
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/attachments/backend/config/attachments/page.tsx +1 -1
- package/src/modules/audit_logs/components/AuditLogsActions.tsx +1 -1
- package/src/modules/auth/frontend/login.tsx +2 -2
- package/src/modules/catalog/widgets/injection/product-bulk-delete/widget.ts +15 -5
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +4 -4
- package/src/modules/communication_channels/i18n/de.json +2 -2
- package/src/modules/communication_channels/i18n/en.json +2 -2
- package/src/modules/communication_channels/i18n/es.json +2 -2
- package/src/modules/communication_channels/i18n/ko.json +2 -2
- package/src/modules/communication_channels/i18n/pl.json +2 -2
- package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +2 -2
- package/src/modules/customers/api/utils.ts +13 -6
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +1 -1
- package/src/modules/customers/components/detail/MiniWeekCalendar.tsx +8 -57
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -1
- package/src/modules/customers/components/detail/legacyActivities.ts +79 -0
- package/src/modules/customers/utils/phoneDuplicates.ts +6 -3
- package/src/modules/data_sync/backend/data-sync/page.tsx +3 -3
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +5 -5
- package/src/modules/entities/api/records.ts +12 -4
- package/src/modules/entities/backend/entities/user/[entityId]/page.tsx +1 -1
- package/src/modules/feature_toggles/components/FeatureToggleOverrideCard.tsx +1 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +1 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +1 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/reset-password/page.tsx +1 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.tsx +1 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/verify/page.tsx +1 -1
- package/src/modules/sales/components/documents/ItemsSection.tsx +13 -0
- package/src/modules/sales/components/documents/lineItemTypes.ts +1 -0
- package/src/modules/sync_excel/widgets/injection/upload-config/widget.client.tsx +1 -1
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +2 -2
- package/src/modules/workflows/backend/definitions/create/page.tsx +1 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3 -3
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
- package/src/modules/workflows/components/NodeEditDialog.tsx +3 -3
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +3 -3
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -1
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/sync_excel/widgets/injection/upload-config/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport type { FieldMapping } from '../../../../data_sync/lib/adapter'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { StatusBadge, type StatusMap } from '@open-mercato/ui/primitives/status-badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@open-mercato/ui/primitives/table'\nimport { AlertTriangle, CheckCircle2, Play, RefreshCw, Upload, XCircle } from 'lucide-react'\nimport {\n buildPeopleSuggestedMapping,\n buildPeopleTargetOptions,\n buildSuggestedMappingSignature,\n findMappingTargetOption,\n SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS,\n type MappingTargetOption,\n type SuggestedMapping,\n} from './target-options'\n\ntype SyncExcelIntegrationContext = {\n formId?: string\n integrationDetailWidgetSpotId?: string\n integrationId?: string\n activeTab?: string\n state?: {\n isEnabled?: boolean\n } | null\n refreshDetail?: () => Promise<void>\n refreshLogs?: () => Promise<void>\n refreshHealthSnapshot?: () => Promise<void>\n}\n\ntype SyncExcelIntegrationData = {\n state?: {\n isEnabled?: boolean\n } | null\n}\n\ntype PreviewRow = Record<string, string | null>\n\ntype UploadResponse = {\n uploadId: string\n filename: string\n mimeType: string\n fileSize: number\n entityType: 'customers.person'\n headers: string[]\n sampleRows: PreviewRow[]\n totalRows: number\n suggestedMapping: SuggestedMapping\n}\n\ntype ImportResponse = {\n runId: string\n progressJobId: string | null\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n}\n\ntype SyncRunDetail = {\n id: string\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n skippedCount: number\n failedCount: number\n lastError: string | null\n progressJobId: string | null\n progressJob: {\n id: string\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n progressPercent: number\n processedCount: number\n totalCount: number | null\n etaSeconds: number | null\n } | null\n}\n\ntype MappingRowState = {\n sourceColumn: string\n targetField: string\n}\n\ntype MappingDiagnostics = {\n duplicateTargets: string[]\n hasNameTarget: boolean\n hasIdentityTarget: boolean\n mappedCount: number\n unmappedCount: number\n}\n\ntype PersistedSessionSnapshot = {\n uploadId: string\n filename: string\n preview: UploadResponse | null\n mappingRows: MappingRowState[]\n matchStrategy: SuggestedMapping['matchStrategy']\n runId: string | null\n progressJobId: string | null\n}\n\nconst ENTITY_TYPE = 'customers.person' as const\nconst SESSION_STORAGE_PREFIX = 'om:sync_excel:session'\nconst RUN_POLL_INTERVAL_MS = 4_000\n\nconst SELECT_CLASS_NAME = 'flex h-9 w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'\n\nfunction buildMappingRows(headers: string[], suggestedMapping: SuggestedMapping): MappingRowState[] {\n const mappedFields = new Map(suggestedMapping.fields.map((field) => [field.externalField, field.localField]))\n return headers.map((header) => ({\n sourceColumn: header,\n targetField: mappedFields.get(header) ?? '',\n }))\n}\n\nfunction buildFieldMapping(sourceColumn: string, targetField: string, targetOptions: MappingTargetOption[]): FieldMapping {\n const matchedOption = findMappingTargetOption(targetOptions, targetField)\n const mappingKind = matchedOption?.mappingKind ?? (targetField.startsWith('cf:') ? 'custom_field' : 'core')\n const dedupeRole = matchedOption?.dedupeRole\n\n return {\n externalField: sourceColumn,\n localField: targetField,\n mappingKind,\n ...(dedupeRole ? { dedupeRole } : {}),\n }\n}\n\nfunction inferMatchStrategy(rows: MappingRowState[]): SuggestedMapping['matchStrategy'] {\n if (rows.some((row) => row.targetField === 'person.externalId')) return 'externalId'\n if (rows.some((row) => row.targetField === 'person.primaryEmail')) return 'email'\n return 'custom'\n}\n\nfunction buildMapping(\n rows: MappingRowState[],\n matchStrategy: SuggestedMapping['matchStrategy'],\n targetOptions: MappingTargetOption[],\n): SuggestedMapping {\n const mappedRows = rows.filter((row) => row.targetField.trim().length > 0)\n const fields = mappedRows.map((row) => buildFieldMapping(row.sourceColumn, row.targetField, targetOptions))\n const resolvedMatchStrategy = matchStrategy === 'custom' ? inferMatchStrategy(rows) : matchStrategy\n const matchField = resolvedMatchStrategy === 'externalId'\n ? 'person.externalId'\n : resolvedMatchStrategy === 'email'\n ? 'person.primaryEmail'\n : undefined\n\n return {\n entityType: ENTITY_TYPE,\n matchStrategy: resolvedMatchStrategy,\n ...(matchField ? { matchField } : {}),\n fields,\n unmappedColumns: rows.filter((row) => row.targetField.trim().length === 0).map((row) => row.sourceColumn),\n }\n}\n\nfunction buildDiagnostics(rows: MappingRowState[]): MappingDiagnostics {\n const duplicateTargets = Array.from(\n rows\n .filter((row) => row.targetField.trim().length > 0)\n .reduce((accumulator, row) => {\n const nextCount = (accumulator.get(row.targetField) ?? 0) + 1\n accumulator.set(row.targetField, nextCount)\n return accumulator\n }, new Map<string, number>())\n .entries(),\n )\n .filter(([, count]) => count > 1)\n .map(([target]) => target)\n\n const hasNameTarget = rows.some((row) => row.targetField === 'person.lastName' || row.targetField === 'person.displayName')\n const hasIdentityTarget = rows.some((row) => row.targetField === 'person.externalId' || row.targetField === 'person.primaryEmail')\n const mappedCount = rows.filter((row) => row.targetField.trim().length > 0).length\n\n return {\n duplicateTargets,\n hasNameTarget,\n hasIdentityTarget,\n mappedCount,\n unmappedCount: rows.length - mappedCount,\n }\n}\n\nfunction formatTargetLabel(\n targetField: string,\n t: ReturnType<typeof useT>,\n targetOptions: MappingTargetOption[],\n): string {\n const option = findMappingTargetOption(targetOptions, targetField)\n if (!option) return targetField\n return option.labelKey ? t(option.labelKey, option.fallback) : option.fallback\n}\n\nfunction getMatchStrategyLabel(\n matchStrategy: SuggestedMapping['matchStrategy'],\n t: ReturnType<typeof useT>,\n): string {\n if (matchStrategy === 'externalId') {\n return t('sync_excel.widget.matchStrategy.externalId', 'Source record ID (Recommended)')\n }\n if (matchStrategy === 'email') {\n return t('sync_excel.widget.matchStrategy.email', 'Email address')\n }\n return t('sync_excel.widget.matchStrategy.custom', 'Custom matching')\n}\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction readApiErrorMessage(result: Record<string, unknown> | null | undefined, fallback: string): string {\n const value = result?.error\n if (typeof value === 'string' && value.trim().length > 0) return value\n return fallback\n}\n\nfunction normalizeStatus(status: SyncRunDetail['status'] | ImportResponse['status'] | null | undefined): 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'idle' {\n if (status === 'pending' || status === 'running' || status === 'completed' || status === 'failed' || status === 'cancelled') {\n return status\n }\n return 'idle'\n}\n\nfunction getSessionStorageKey(integrationId: string | undefined): string {\n return `${SESSION_STORAGE_PREFIX}:${integrationId ?? 'sync_excel'}`\n}\n\nfunction readPersistedSessionSnapshot(integrationId: string | undefined): PersistedSessionSnapshot | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(getSessionStorageKey(integrationId))\n if (!raw) return null\n const parsed = JSON.parse(raw) as Partial<PersistedSessionSnapshot> | null\n if (!parsed || typeof parsed !== 'object') return null\n if (typeof parsed.uploadId !== 'string' || parsed.uploadId.trim().length === 0) return null\n if (typeof parsed.filename !== 'string') return null\n if (!Array.isArray(parsed.mappingRows)) return null\n if (parsed.matchStrategy !== 'externalId' && parsed.matchStrategy !== 'email' && parsed.matchStrategy !== 'custom') {\n return null\n }\n const preview = (\n parsed.preview\n && typeof parsed.preview === 'object'\n && typeof parsed.preview.uploadId === 'string'\n && parsed.preview.uploadId.trim().length > 0\n && typeof parsed.preview.filename === 'string'\n && Array.isArray(parsed.preview.headers)\n && Array.isArray(parsed.preview.sampleRows)\n && typeof parsed.preview.totalRows === 'number'\n && typeof parsed.preview.fileSize === 'number'\n && parsed.preview.entityType === ENTITY_TYPE\n && parsed.preview.suggestedMapping\n )\n ? parsed.preview as UploadResponse\n : null\n\n return {\n uploadId: parsed.uploadId,\n filename: parsed.filename,\n preview,\n mappingRows: parsed.mappingRows\n .filter((row): row is MappingRowState => (\n Boolean(row)\n && typeof row.sourceColumn === 'string'\n && typeof row.targetField === 'string'\n )),\n matchStrategy: parsed.matchStrategy,\n runId: typeof parsed.runId === 'string' && parsed.runId.trim().length > 0 ? parsed.runId : null,\n progressJobId: typeof parsed.progressJobId === 'string' && parsed.progressJobId.trim().length > 0 ? parsed.progressJobId : null,\n }\n } catch {\n return null\n }\n}\n\nfunction writePersistedSessionSnapshot(\n integrationId: string | undefined,\n snapshot: PersistedSessionSnapshot | null,\n): void {\n if (typeof window === 'undefined') return\n try {\n const key = getSessionStorageKey(integrationId)\n if (!snapshot) {\n window.sessionStorage.removeItem(key)\n return\n }\n window.sessionStorage.setItem(key, JSON.stringify(snapshot))\n } catch {\n // sessionStorage can be unavailable in private/locked-down browser contexts; persistence is best-effort.\n }\n}\n\nexport default function SyncExcelUploadConfigWidget({\n context,\n data,\n}: InjectionWidgetComponentProps<SyncExcelIntegrationContext, SyncExcelIntegrationData>) {\n const pathname = usePathname()\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const { data: customFieldDefs = [] } = useCustomFieldDefs([...SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS])\n const [selectedFile, setSelectedFile] = React.useState<File | null>(null)\n const [preview, setPreview] = React.useState<UploadResponse | null>(null)\n const [mappingRows, setMappingRows] = React.useState<MappingRowState[]>([])\n const [matchStrategy, setMatchStrategy] = React.useState<SuggestedMapping['matchStrategy']>('custom')\n const [isMappingDirty, setIsMappingDirty] = React.useState(false)\n const [runDetail, setRunDetail] = React.useState<SyncRunDetail | null>(null)\n const [runId, setRunId] = React.useState<string | null>(null)\n const [progressJobId, setProgressJobId] = React.useState<string | null>(null)\n const [isUploading, setIsUploading] = React.useState(false)\n const [isRefreshingPreview, setIsRefreshingPreview] = React.useState(false)\n const [isStartingImport, setIsStartingImport] = React.useState(false)\n const [isCancelling, setIsCancelling] = React.useState(false)\n const [isRestoringSession, setIsRestoringSession] = React.useState(true)\n const mutationContextId = `${context.formId ?? 'sync-excel'}:sync-excel-import`\n const lastAppliedSuggestionSignatureRef = React.useRef<string | null>(null)\n const restoringSnapshotRef = React.useRef<PersistedSessionSnapshot | null>(null)\n const lastRestoreRequestKeyRef = React.useRef<string | null>(null)\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n spotId: context.integrationDetailWidgetSpotId,\n })\n\n const targetOptions = React.useMemo(\n () => buildPeopleTargetOptions(customFieldDefs),\n [customFieldDefs],\n )\n const previewSuggestion = React.useMemo(\n () => preview ? buildPeopleSuggestedMapping(preview.headers, preview.suggestedMapping, customFieldDefs) : null,\n [customFieldDefs, preview],\n )\n const resolvedIntegrationEnabled = data?.state?.isEnabled ?? context.state?.isEnabled ?? true\n const diagnostics = React.useMemo(() => buildDiagnostics(mappingRows), [mappingRows])\n const canStartImport = Boolean(preview)\n && diagnostics.duplicateTargets.length === 0\n && diagnostics.hasNameTarget\n && diagnostics.hasIdentityTarget\n && mappingRows.some((row) => row.targetField.trim().length > 0)\n const importStatus = normalizeStatus(runDetail?.status ?? (runId ? 'pending' : null))\n const progressValue = runDetail?.progressJob?.progressPercent ?? 0\n const isRunActive = importStatus === 'pending' || importStatus === 'running'\n const searchParamsKey = searchParams?.toString() ?? ''\n const uploadIdFromUrl = searchParams?.get('uploadId') ?? null\n const runIdFromUrl = searchParams?.get('runId') ?? null\n const shouldHideInteractiveContent = isRestoringSession && Boolean(uploadIdFromUrl)\n const hasExistingRunForCurrentUpload = Boolean(preview?.uploadId && runId)\n const hasSafeDedupeStrategy = matchStrategy === 'externalId' || matchStrategy === 'email'\n\n const replaceQueryParams = React.useCallback((updates: Record<string, string | null | undefined>) => {\n const params = new URLSearchParams(searchParamsKey)\n for (const [key, value] of Object.entries(updates)) {\n if (typeof value === 'string' && value.trim().length > 0) {\n params.set(key, value)\n } else {\n params.delete(key)\n }\n }\n const query = params.toString()\n if (query === searchParamsKey) return\n router.replace(query ? `${pathname}?${query}` : pathname)\n }, [pathname, router, searchParamsKey])\n\n const clearPersistedSession = React.useCallback(() => {\n writePersistedSessionSnapshot(context.integrationId, null)\n replaceQueryParams({ uploadId: null, runId: null })\n }, [context.integrationId, replaceQueryParams])\n\n const persistCurrentSession = React.useCallback((next?: Partial<PersistedSessionSnapshot>) => {\n const persistedPreview = next?.preview ?? preview ?? null\n const uploadId = next?.uploadId ?? persistedPreview?.uploadId ?? null\n const filename = next?.filename ?? persistedPreview?.filename ?? null\n if (!uploadId || !filename) {\n writePersistedSessionSnapshot(context.integrationId, null)\n return\n }\n\n writePersistedSessionSnapshot(context.integrationId, {\n uploadId,\n filename,\n preview: persistedPreview,\n mappingRows: next?.mappingRows ?? mappingRows,\n matchStrategy: next?.matchStrategy ?? matchStrategy,\n runId: next?.runId ?? runId,\n progressJobId: next?.progressJobId ?? progressJobId,\n })\n }, [context.integrationId, mappingRows, matchStrategy, preview, progressJobId, runId])\n\n const syncPreviewState = React.useCallback((nextPreview: UploadResponse, options?: {\n preserveManualState?: boolean\n restoredSnapshot?: PersistedSessionSnapshot | null\n }) => {\n const nextSuggestedMapping = buildPeopleSuggestedMapping(nextPreview.headers, nextPreview.suggestedMapping, customFieldDefs)\n setPreview(nextPreview)\n\n const restoredSnapshot = options?.restoredSnapshot\n const shouldRestoreSnapshot = Boolean(\n restoredSnapshot\n && restoredSnapshot.uploadId === nextPreview.uploadId\n && restoredSnapshot.mappingRows.length > 0,\n )\n\n if (shouldRestoreSnapshot) {\n setMappingRows(restoredSnapshot!.mappingRows)\n setMatchStrategy(restoredSnapshot!.matchStrategy)\n setRunId(restoredSnapshot!.runId)\n setProgressJobId(restoredSnapshot!.progressJobId)\n setIsMappingDirty(true)\n lastAppliedSuggestionSignatureRef.current = null\n persistCurrentSession({\n uploadId: nextPreview.uploadId,\n filename: nextPreview.filename,\n preview: nextPreview,\n mappingRows: restoredSnapshot!.mappingRows,\n matchStrategy: restoredSnapshot!.matchStrategy,\n runId: restoredSnapshot!.runId,\n progressJobId: restoredSnapshot!.progressJobId,\n })\n replaceQueryParams({\n uploadId: nextPreview.uploadId,\n runId: restoredSnapshot!.runId,\n })\n return\n }\n\n if (options?.preserveManualState && preview?.uploadId === nextPreview.uploadId) {\n persistCurrentSession({\n uploadId: nextPreview.uploadId,\n filename: nextPreview.filename,\n preview: nextPreview,\n })\n replaceQueryParams({ uploadId: nextPreview.uploadId })\n return\n }\n\n const nextRows = buildMappingRows(nextPreview.headers, nextSuggestedMapping)\n setMappingRows(nextRows)\n setMatchStrategy(nextSuggestedMapping.matchStrategy)\n setIsMappingDirty(false)\n lastAppliedSuggestionSignatureRef.current = buildSuggestedMappingSignature(nextPreview.headers, nextSuggestedMapping)\n persistCurrentSession({\n uploadId: nextPreview.uploadId,\n filename: nextPreview.filename,\n preview: nextPreview,\n mappingRows: nextRows,\n matchStrategy: nextSuggestedMapping.matchStrategy,\n runId: null,\n progressJobId: null,\n })\n replaceQueryParams({\n uploadId: nextPreview.uploadId,\n runId: null,\n })\n }, [customFieldDefs, persistCurrentSession, preview?.uploadId, replaceQueryParams])\n\n React.useEffect(() => {\n if (!preview || !previewSuggestion) return\n if (isMappingDirty) return\n const nextSignature = buildSuggestedMappingSignature(preview.headers, previewSuggestion)\n if (lastAppliedSuggestionSignatureRef.current === nextSignature) return\n setMappingRows(buildMappingRows(preview.headers, previewSuggestion))\n setMatchStrategy(previewSuggestion.matchStrategy)\n lastAppliedSuggestionSignatureRef.current = nextSignature\n }, [isMappingDirty, preview, previewSuggestion])\n\n const refreshRunDetail = React.useCallback(async (currentRunId: string) => {\n const call = await apiCall<SyncRunDetail>(`/api/data_sync/runs/${encodeURIComponent(currentRunId)}`, undefined, { fallback: null })\n if (call.ok && call.result) {\n setRunDetail(call.result)\n setProgressJobId(call.result.progressJobId ?? null)\n persistCurrentSession({\n runId: currentRunId,\n progressJobId: call.result.progressJobId ?? null,\n })\n return call.result\n }\n return null\n }, [persistCurrentSession])\n\n React.useEffect(() => {\n if (!preview?.uploadId) return\n persistCurrentSession()\n }, [mappingRows, matchStrategy, persistCurrentSession, preview?.uploadId, progressJobId, runId])\n\n React.useEffect(() => {\n const persistedSnapshot = readPersistedSessionSnapshot(context.integrationId)\n const resolvedUploadId = uploadIdFromUrl ?? persistedSnapshot?.uploadId ?? null\n const resolvedRunId = runIdFromUrl ?? persistedSnapshot?.runId ?? null\n\n if (!resolvedUploadId) {\n restoringSnapshotRef.current = null\n lastRestoreRequestKeyRef.current = null\n setIsRestoringSession(false)\n return\n }\n\n if (!uploadIdFromUrl && persistedSnapshot?.uploadId === resolvedUploadId) {\n replaceQueryParams({\n uploadId: resolvedUploadId,\n runId: resolvedRunId,\n })\n }\n\n if (preview?.uploadId === resolvedUploadId && mappingRows.length > 0) {\n if (resolvedRunId && runId !== resolvedRunId) {\n setRunId(resolvedRunId)\n }\n setIsRestoringSession(false)\n return\n }\n\n const restoreRequestKey = `${resolvedUploadId}:${resolvedRunId ?? ''}`\n if (lastRestoreRequestKeyRef.current === restoreRequestKey) {\n setIsRestoringSession(false)\n return\n }\n\n lastRestoreRequestKeyRef.current = restoreRequestKey\n\n let cancelled = false\n restoringSnapshotRef.current = persistedSnapshot\n const persistedPreview = restoringSnapshotRef.current?.preview?.uploadId === resolvedUploadId\n ? restoringSnapshotRef.current.preview\n : null\n\n if (persistedPreview) {\n syncPreviewState(persistedPreview, {\n restoredSnapshot: restoringSnapshotRef.current,\n })\n setIsRestoringSession(false)\n } else {\n setIsRestoringSession(true)\n }\n\n const restoreSession = async () => {\n const call = await apiCall<UploadResponse>(\n `/api/sync_excel/preview?uploadId=${encodeURIComponent(resolvedUploadId)}&entityType=${encodeURIComponent(ENTITY_TYPE)}`,\n undefined,\n { fallback: null },\n )\n\n if (cancelled) return\n\n if (!call.ok || !call.result) {\n setPreview(null)\n setMappingRows([])\n setMatchStrategy('custom')\n setRunId(null)\n setRunDetail(null)\n setProgressJobId(null)\n clearPersistedSession()\n lastRestoreRequestKeyRef.current = null\n setIsRestoringSession(false)\n return\n }\n\n syncPreviewState(call.result, {\n restoredSnapshot: restoringSnapshotRef.current,\n })\n\n if (resolvedRunId && !(restoringSnapshotRef.current?.runId)) {\n setRunId(resolvedRunId)\n }\n\n setIsRestoringSession(false)\n }\n\n void restoreSession()\n\n return () => {\n cancelled = true\n }\n }, [clearPersistedSession, context.integrationId, mappingRows.length, preview?.uploadId, replaceQueryParams, runId, runIdFromUrl, syncPreviewState, uploadIdFromUrl])\n\n React.useEffect(() => {\n if (!runId) return\n\n let cancelled = false\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n\n const poll = async () => {\n const detail = await refreshRunDetail(runId)\n if (cancelled) return\n if (context.activeTab === 'logs') {\n await context.refreshLogs?.()\n }\n if (context.activeTab === 'health' || normalizeStatus(detail?.status) !== 'idle') {\n await context.refreshHealthSnapshot?.()\n }\n const status = normalizeStatus(detail?.status)\n if (status === 'pending' || status === 'running') {\n timeoutId = setTimeout(() => {\n void poll()\n }, RUN_POLL_INTERVAL_MS)\n }\n }\n\n void poll()\n\n return () => {\n cancelled = true\n if (timeoutId) clearTimeout(timeoutId)\n }\n }, [context.activeTab, context.refreshHealthSnapshot, context.refreshLogs, refreshRunDetail, runId])\n\n const handleUpload = React.useCallback(async () => {\n if (!selectedFile) {\n flash(t('sync_excel.widget.messages.fileRequired', 'Select a CSV file first.'), 'error')\n return\n }\n\n setIsUploading(true)\n try {\n const formData = new FormData()\n formData.set('file', selectedFile)\n formData.set('entityType', ENTITY_TYPE)\n\n const call = await runMutation({\n operation: () => apiCall<UploadResponse>('/api/sync_excel/upload', {\n method: 'POST',\n body: formData,\n }, { fallback: null }),\n mutationPayload: {\n integrationId: context.integrationId ?? 'sync_excel',\n entityType: ENTITY_TYPE,\n fileName: selectedFile.name,\n },\n context: {\n ...context,\n operation: 'create',\n actionId: 'sync-excel-upload',\n },\n })\n\n if (!call.ok || !call.result) {\n flash(\n readApiErrorMessage(call.result as Record<string, unknown> | null, t('sync_excel.widget.messages.uploadError', 'Failed to upload CSV file.')),\n 'error',\n )\n return\n }\n\n syncPreviewState(call.result)\n setSelectedFile(null)\n setRunId(null)\n setRunDetail(null)\n setProgressJobId(null)\n flash(t('sync_excel.widget.messages.uploadSuccess', 'CSV preview is ready.'), 'success')\n } catch {\n flash(t('sync_excel.widget.messages.uploadError', 'Failed to upload CSV file.'), 'error')\n } finally {\n setIsUploading(false)\n }\n }, [context, runMutation, selectedFile, syncPreviewState, t])\n\n const handleRefreshPreview = React.useCallback(async () => {\n if (!preview) return\n setIsRefreshingPreview(true)\n try {\n const call = await apiCall<UploadResponse>(\n `/api/sync_excel/preview?uploadId=${encodeURIComponent(preview.uploadId)}&entityType=${encodeURIComponent(ENTITY_TYPE)}`,\n undefined,\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n flash(t('sync_excel.widget.messages.previewError', 'Failed to refresh preview.'), 'error')\n return\n }\n syncPreviewState(call.result, { preserveManualState: true })\n flash(t('sync_excel.widget.messages.previewRefreshed', 'Preview refreshed.'), 'success')\n } catch {\n flash(t('sync_excel.widget.messages.previewError', 'Failed to refresh preview.'), 'error')\n } finally {\n setIsRefreshingPreview(false)\n }\n }, [preview, syncPreviewState, t])\n\n const handleTargetFieldChange = React.useCallback((sourceColumn: string, targetField: string) => {\n setIsMappingDirty(true)\n setMappingRows((current) => current.map((row) => row.sourceColumn === sourceColumn ? { ...row, targetField } : row))\n }, [])\n\n const handleResetToSuggested = React.useCallback(() => {\n if (!preview || !previewSuggestion) return\n setMappingRows(buildMappingRows(preview.headers, previewSuggestion))\n setMatchStrategy(previewSuggestion.matchStrategy)\n setIsMappingDirty(false)\n lastAppliedSuggestionSignatureRef.current = buildSuggestedMappingSignature(preview.headers, previewSuggestion)\n flash(t('sync_excel.widget.messages.mappingReset', 'Suggested mapping restored.'), 'success')\n }, [preview, previewSuggestion, t])\n\n const handleStartImport = React.useCallback(async () => {\n if (!preview) return\n\n const mapping = buildMapping(mappingRows, matchStrategy, targetOptions)\n setIsStartingImport(true)\n\n try {\n const call = await runMutation({\n operation: () => apiCall<ImportResponse>('/api/sync_excel/import', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n uploadId: preview.uploadId,\n entityType: ENTITY_TYPE,\n mapping,\n }),\n }, { fallback: null }),\n mutationPayload: {\n integrationId: context.integrationId ?? 'sync_excel',\n uploadId: preview.uploadId,\n entityType: ENTITY_TYPE,\n mapping,\n },\n context: {\n ...context,\n operation: 'create',\n actionId: 'sync-excel-start-import',\n },\n })\n\n if (!call.ok || !call.result) {\n flash(\n readApiErrorMessage(call.result as Record<string, unknown> | null, t('sync_excel.widget.messages.importError', 'Failed to start import run.')),\n 'error',\n )\n return\n }\n\n setRunId(call.result.runId)\n setProgressJobId(call.result.progressJobId)\n replaceQueryParams({\n uploadId: preview.uploadId,\n runId: call.result.runId,\n })\n persistCurrentSession({\n uploadId: preview.uploadId,\n filename: preview.filename,\n runId: call.result.runId,\n progressJobId: call.result.progressJobId,\n })\n flash(t('sync_excel.widget.messages.importStarted', 'Import run started.'), 'success')\n await refreshRunDetail(call.result.runId)\n await context.refreshLogs?.()\n await context.refreshHealthSnapshot?.()\n } catch {\n flash(t('sync_excel.widget.messages.importError', 'Failed to start import run.'), 'error')\n } finally {\n setIsStartingImport(false)\n }\n }, [context, mappingRows, matchStrategy, persistCurrentSession, preview, refreshRunDetail, replaceQueryParams, runMutation, t, targetOptions])\n\n const handleCancelRun = React.useCallback(async () => {\n if (!runId) return\n\n setIsCancelling(true)\n try {\n const call = await runMutation({\n operation: () => apiCall<{ ok?: boolean }>(`/api/data_sync/runs/${encodeURIComponent(runId)}/cancel`, {\n method: 'POST',\n }, { fallback: null }),\n mutationPayload: {\n integrationId: context.integrationId ?? 'sync_excel',\n runId,\n },\n context: {\n ...context,\n operation: 'update',\n actionId: 'sync-excel-cancel-import',\n },\n })\n\n if (!call.ok) {\n flash(t('sync_excel.widget.messages.cancelError', 'Failed to cancel import run.'), 'error')\n return\n }\n\n flash(t('sync_excel.widget.messages.cancelSuccess', 'Import run cancelled.'), 'success')\n await refreshRunDetail(runId)\n await context.refreshLogs?.()\n await context.refreshHealthSnapshot?.()\n } catch {\n flash(t('sync_excel.widget.messages.cancelError', 'Failed to cancel import run.'), 'error')\n } finally {\n setIsCancelling(false)\n }\n }, [context, refreshRunDetail, runId, runMutation, t])\n\n const handleRefreshAll = React.useCallback(async () => {\n if (!runId) return\n await refreshRunDetail(runId)\n await context.refreshLogs?.()\n await context.refreshHealthSnapshot?.()\n }, [context, refreshRunDetail, runId])\n\n const firstSampleRow = preview?.sampleRows[0] ?? null\n\n return (\n <div className=\"space-y-6\">\n {isRestoringSession ? (\n <Card>\n <CardContent className=\"flex items-center gap-3 py-6\">\n <Spinner className=\"size-4\" />\n <div className=\"text-sm text-muted-foreground\">\n {t('sync_excel.widget.messages.restoringSession', 'Restoring the last CSV session...')}\n </div>\n </CardContent>\n </Card>\n ) : null}\n\n {!resolvedIntegrationEnabled ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.disabled.title', 'Integration disabled')}\n message={t('sync_excel.widget.disabled.message', 'Enable this integration to upload CSV files and start imports.')}\n />\n ) : null}\n\n {!shouldHideInteractiveContent ? (\n <Card>\n <CardHeader>\n <CardTitle>{t('sync_excel.widget.upload.title', 'Upload CSV')}</CardTitle>\n <CardDescription>\n {t('sync_excel.widget.upload.description', 'Upload a CSV file, review the suggested people mapping, and start a background import run.')}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"sync-excel-upload-file\">{t('sync_excel.widget.upload.fileLabel', 'CSV file')}</Label>\n <Input\n id=\"sync-excel-upload-file\"\n type=\"file\"\n accept=\".csv,text/csv\"\n onChange={(event) => setSelectedFile(event.target.files?.[0] ?? null)}\n disabled={isUploading || isRunActive || isRestoringSession}\n />\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button type=\"button\" onClick={() => void handleUpload()} disabled={!selectedFile || isUploading || isRunActive || isRestoringSession}>\n {isUploading ? <Spinner className=\"mr-2 size-4\" /> : <Upload className=\"mr-2 size-4\" />}\n {isUploading\n ? t('sync_excel.widget.actions.uploadPending', 'Uploading...')\n : t('sync_excel.widget.actions.upload', 'Upload and preview')}\n </Button>\n {preview ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => void handleRefreshPreview()} disabled={isRefreshingPreview || isUploading || isRunActive || isRestoringSession}>\n {isRefreshingPreview ? <Spinner className=\"mr-2 size-4\" /> : <RefreshCw className=\"mr-2 size-4\" />}\n {isRefreshingPreview\n ? t('sync_excel.widget.actions.refreshPending', 'Refreshing...')\n : t('sync_excel.widget.actions.refresh', 'Refresh preview')}\n </Button>\n ) : null}\n </div>\n\n {selectedFile ? (\n <div className=\"text-sm text-muted-foreground\">\n {t('sync_excel.widget.upload.selectedFile', 'Selected file')}: <span className=\"font-medium text-foreground\">{selectedFile.name}</span>\n </div>\n ) : null}\n </CardContent>\n </Card>\n ) : null}\n\n {preview && !shouldHideInteractiveContent ? (\n <Card>\n <CardHeader>\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <CardTitle>{t('sync_excel.widget.preview.title', 'Preview and mapping')}</CardTitle>\n <CardDescription>\n {t('sync_excel.widget.preview.description', 'Every source column stays visible here. Leave a target empty to keep the column unmapped for now.')}\n </CardDescription>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"outline\">{preview.filename}</Badge>\n <Badge variant=\"outline\">{t('sync_excel.widget.preview.rows', '{count} rows', { count: preview.totalRows })}</Badge>\n <Badge variant=\"outline\">{formatFileSize(preview.fileSize)}</Badge>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <MetricCard\n label={t('sync_excel.widget.metrics.mapped', 'Mapped columns')}\n value={String(diagnostics.mappedCount)}\n />\n <MetricCard\n label={t('sync_excel.widget.metrics.unmapped', 'Unmapped columns')}\n value={String(diagnostics.unmappedCount)}\n />\n <MetricCard\n label={t('sync_excel.widget.metrics.identity', 'Identity strategy')}\n value={getMatchStrategyLabel(matchStrategy, t)}\n />\n <MetricCard\n label={t('sync_excel.widget.metrics.entity', 'Target entity')}\n value={ENTITY_TYPE}\n />\n </div>\n\n <div className=\"grid gap-4 lg:grid-cols-[minmax(0,1fr)_280px]\">\n <div className=\"overflow-hidden rounded-lg border\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('sync_excel.widget.mapping.sourceColumn', 'Source column')}</TableHead>\n <TableHead>{t('sync_excel.widget.mapping.sampleValue', 'Sample value')}</TableHead>\n <TableHead>{t('sync_excel.widget.mapping.targetField', 'Target field')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {mappingRows.map((row) => (\n <TableRow key={row.sourceColumn}>\n <TableCell className=\"font-medium\">{row.sourceColumn}</TableCell>\n <TableCell className=\"max-w-[260px] truncate text-muted-foreground\">\n {firstSampleRow?.[row.sourceColumn] ?? '\u2014'}\n </TableCell>\n <TableCell>\n <select\n className={SELECT_CLASS_NAME}\n value={row.targetField}\n onChange={(event) => handleTargetFieldChange(row.sourceColumn, event.target.value)}\n disabled={isStartingImport || isRunActive}\n >\n <option value=\"\">{t('sync_excel.mapping.targets.ignore', 'Leave unmapped')}</option>\n {targetOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.labelKey ? t(option.labelKey, option.fallback) : option.fallback}\n </option>\n ))}\n </select>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"sync-excel-match-strategy\">\n {t('sync_excel.widget.matchStrategy.label', 'How to match existing people')}\n </Label>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'sync_excel.widget.matchStrategy.help',\n 'This decides whether imported rows update existing people or create new ones.',\n )}\n </p>\n <select\n id=\"sync-excel-match-strategy\"\n className={SELECT_CLASS_NAME}\n value={matchStrategy}\n onChange={(event) => {\n setIsMappingDirty(true)\n setMatchStrategy(event.target.value as SuggestedMapping['matchStrategy'])\n }}\n disabled={isStartingImport || isRunActive}\n >\n <option value=\"externalId\">\n {t('sync_excel.widget.matchStrategy.externalId', 'Source record ID (Recommended)')}\n </option>\n <option value=\"email\">{t('sync_excel.widget.matchStrategy.email', 'Email address')}</option>\n <option value=\"custom\">{t('sync_excel.widget.matchStrategy.custom', 'Custom matching')}</option>\n </select>\n <p className=\"text-sm text-muted-foreground\">\n {matchStrategy === 'externalId'\n ? t(\n 'sync_excel.widget.matchStrategy.externalIdDescription',\n 'Best for repeated imports from the same source. Future imports update the same people instead of creating duplicates.',\n )\n : matchStrategy === 'email'\n ? t(\n 'sync_excel.widget.matchStrategy.emailDescription',\n 'Matches people by email. Works best when each person has one reliable, unique email address.',\n )\n : t(\n 'sync_excel.widget.matchStrategy.customDescription',\n 'Advanced option. Use only if you know how existing records should be identified in this import.',\n )}\n </p>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={handleResetToSuggested} disabled={isStartingImport || isRunActive}>\n <RefreshCw className=\"mr-2 size-4\" />\n {t('sync_excel.widget.actions.reset', 'Reset to suggestion')}\n </Button>\n <Button type=\"button\" onClick={() => void handleStartImport()} disabled={!canStartImport || isStartingImport || isRunActive}>\n {isStartingImport ? <Spinner className=\"mr-2 size-4\" /> : <Play className=\"mr-2 size-4\" />}\n {isStartingImport\n ? t('sync_excel.widget.actions.startPending', 'Starting...')\n : t('sync_excel.widget.actions.start', 'Start import')}\n </Button>\n </div>\n\n {matchStrategy === 'custom' ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.matchStrategy.customWarningTitle', 'Use custom matching with care')}\n message={t(\n 'sync_excel.widget.matchStrategy.customWarningMessage',\n 'Custom matching may create duplicates if the mapped fields do not uniquely identify a person.',\n )}\n />\n ) : null}\n\n {hasExistingRunForCurrentUpload ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.reimportTitle', 'This upload already has a run')}\n message={t(\n 'sync_excel.widget.validation.reimportMessage',\n 'Starting another import from the same uploaded CSV may create duplicates unless your matching strategy reliably identifies existing people.',\n )}\n />\n ) : null}\n\n {!hasSafeDedupeStrategy ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.duplicateRiskTitle', 'Duplicate risk')}\n message={t(\n 'sync_excel.widget.validation.duplicateRiskMessage',\n 'This mapping does not use the recommended External ID or Email matching strategy. Re-importing the same dataset may create duplicate people.',\n )}\n />\n ) : null}\n\n {diagnostics.duplicateTargets.length > 0 ? (\n <StatusAlert\n variant=\"error\"\n title={t('sync_excel.widget.validation.duplicateTargetsTitle', 'Duplicate target fields')}\n message={t(\n 'sync_excel.widget.validation.duplicateTargetsMessage',\n 'Each target field can only be mapped once in this foundation slice: {targets}',\n { targets: diagnostics.duplicateTargets.map((target) => formatTargetLabel(target, t, targetOptions)).join(', ') },\n )}\n />\n ) : null}\n\n {!diagnostics.hasIdentityTarget ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.identityTitle', 'Map an identity field')}\n message={t('sync_excel.widget.validation.identityMessage', 'Map either External ID or Primary email so reimports can match existing people.')}\n />\n ) : null}\n\n {!diagnostics.hasNameTarget ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.nameTitle', 'Map a name field')}\n message={t('sync_excel.widget.validation.nameMessage', 'Map Last name or Display name so the importer can build person records.')}\n />\n ) : null}\n\n {diagnostics.hasIdentityTarget && diagnostics.hasNameTarget && diagnostics.duplicateTargets.length === 0 ? (\n <StatusAlert\n variant=\"info\"\n title={t('sync_excel.widget.validation.readyTitle', 'Ready to import')}\n message={t('sync_excel.widget.validation.readyMessage', 'The current mapping is valid for the first customers.person slice.')}\n />\n ) : null}\n </div>\n </div>\n </CardContent>\n </Card>\n ) : null}\n\n {runId && !shouldHideInteractiveContent ? (\n <Card>\n <CardHeader>\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <CardTitle>{t('sync_excel.widget.run.title', 'Import run status')}</CardTitle>\n <CardDescription>\n {t('sync_excel.widget.run.description', 'The import runs in the background through Data Sync and updates this view as progress changes.')}\n </CardDescription>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <RunStatusBadge status={importStatus} t={t} />\n {progressJobId ? <Badge variant=\"outline\">{progressJobId}</Badge> : null}\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-muted-foreground\">{t('sync_excel.widget.run.progressLabel', 'Progress')}</span>\n <span className=\"font-medium\">{progressValue}%</span>\n </div>\n <Progress value={progressValue} className=\"h-2.5\" />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-5\">\n <MetricCard label={t('sync_excel.widget.run.metrics.created', 'Created')} value={String(runDetail?.createdCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.updated', 'Updated')} value={String(runDetail?.updatedCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.skipped', 'Skipped')} value={String(runDetail?.skippedCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.failed', 'Failed')} value={String(runDetail?.failedCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.processed', 'Processed')} value={String(runDetail?.progressJob?.processedCount ?? 0)} />\n </div>\n\n {runDetail?.lastError ? (\n <StatusAlert\n variant=\"error\"\n title={t('sync_excel.widget.run.errorTitle', 'Run error')}\n message={runDetail.lastError}\n />\n ) : null}\n\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => void handleRefreshAll()} disabled={isRefreshingPreview || isRestoringSession}>\n <RefreshCw className=\"mr-2 size-4\" />\n {t('sync_excel.widget.actions.refreshRun', 'Refresh run status')}\n </Button>\n {isRunActive ? (\n <Button type=\"button\" variant=\"destructive\" onClick={() => void handleCancelRun()} disabled={isCancelling}>\n {isCancelling ? <Spinner className=\"mr-2 size-4\" /> : <XCircle className=\"mr-2 size-4\" />}\n {isCancelling\n ? t('sync_excel.widget.actions.cancelPending', 'Cancelling...')\n : t('sync_excel.widget.actions.cancel', 'Cancel run')}\n </Button>\n ) : null}\n </div>\n </CardContent>\n </Card>\n ) : null}\n </div>\n )\n}\n\nfunction StatusAlert({\n variant,\n title,\n message,\n}: {\n variant: 'warning' | 'error' | 'info'\n title: string\n message: string\n}) {\n return (\n <Alert variant={variant === 'error' ? 'destructive' : variant}>\n <AlertTitle>{title}</AlertTitle>\n <AlertDescription>{message}</AlertDescription>\n </Alert>\n )\n}\n\nfunction MetricCard({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"rounded-lg border bg-muted/30 p-4\">\n <div className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">{label}</div>\n <div className=\"mt-2 text-lg font-semibold text-foreground\">{value}</div>\n </div>\n )\n}\n\ntype RunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'idle'\n\nexport const RUN_STATUS_BADGE_VARIANTS: StatusMap<RunStatus> = {\n completed: 'success',\n failed: 'error',\n cancelled: 'neutral',\n pending: 'info',\n running: 'info',\n idle: 'neutral',\n}\n\nexport function RunStatusBadge({\n status,\n t,\n}: {\n status: RunStatus\n t: ReturnType<typeof useT>\n}) {\n const variant = RUN_STATUS_BADGE_VARIANTS[status] ?? 'neutral'\n\n if (status === 'completed') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <CheckCircle2 className=\"size-3.5\" />\n {t('sync_excel.widget.run.status.completed', 'Completed')}\n </StatusBadge>\n )\n }\n\n if (status === 'failed') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <AlertTriangle className=\"size-3.5\" />\n {t('sync_excel.widget.run.status.failed', 'Failed')}\n </StatusBadge>\n )\n }\n\n if (status === 'cancelled') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <XCircle className=\"size-3.5\" />\n {t('sync_excel.widget.run.status.cancelled', 'Cancelled')}\n </StatusBadge>\n )\n }\n\n if (status === 'pending' || status === 'running') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <RefreshCw className={cn('size-3.5', status === 'running' ? 'animate-spin' : '')} />\n {status === 'pending'\n ? t('sync_excel.widget.run.status.pending', 'Pending')\n : t('sync_excel.widget.run.status.running', 'Running')}\n </StatusBadge>\n )\n }\n\n return (\n <StatusBadge variant={variant}>\n {t('sync_excel.widget.run.status.idle', 'Idle')}\n </StatusBadge>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation'\nimport type { InjectionWidgetComponentProps } from '@open-mercato/shared/modules/widgets/injection'\nimport type { FieldMapping } from '../../../../data_sync/lib/adapter'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { StatusBadge, type StatusMap } from '@open-mercato/ui/primitives/status-badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Progress } from '@open-mercato/ui/primitives/progress'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@open-mercato/ui/primitives/table'\nimport { AlertTriangle, CheckCircle2, Play, RefreshCw, Upload, XCircle } from 'lucide-react'\nimport {\n buildPeopleSuggestedMapping,\n buildPeopleTargetOptions,\n buildSuggestedMappingSignature,\n findMappingTargetOption,\n SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS,\n type MappingTargetOption,\n type SuggestedMapping,\n} from './target-options'\n\ntype SyncExcelIntegrationContext = {\n formId?: string\n integrationDetailWidgetSpotId?: string\n integrationId?: string\n activeTab?: string\n state?: {\n isEnabled?: boolean\n } | null\n refreshDetail?: () => Promise<void>\n refreshLogs?: () => Promise<void>\n refreshHealthSnapshot?: () => Promise<void>\n}\n\ntype SyncExcelIntegrationData = {\n state?: {\n isEnabled?: boolean\n } | null\n}\n\ntype PreviewRow = Record<string, string | null>\n\ntype UploadResponse = {\n uploadId: string\n filename: string\n mimeType: string\n fileSize: number\n entityType: 'customers.person'\n headers: string[]\n sampleRows: PreviewRow[]\n totalRows: number\n suggestedMapping: SuggestedMapping\n}\n\ntype ImportResponse = {\n runId: string\n progressJobId: string | null\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n}\n\ntype SyncRunDetail = {\n id: string\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused'\n createdCount: number\n updatedCount: number\n skippedCount: number\n failedCount: number\n lastError: string | null\n progressJobId: string | null\n progressJob: {\n id: string\n status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled'\n progressPercent: number\n processedCount: number\n totalCount: number | null\n etaSeconds: number | null\n } | null\n}\n\ntype MappingRowState = {\n sourceColumn: string\n targetField: string\n}\n\ntype MappingDiagnostics = {\n duplicateTargets: string[]\n hasNameTarget: boolean\n hasIdentityTarget: boolean\n mappedCount: number\n unmappedCount: number\n}\n\ntype PersistedSessionSnapshot = {\n uploadId: string\n filename: string\n preview: UploadResponse | null\n mappingRows: MappingRowState[]\n matchStrategy: SuggestedMapping['matchStrategy']\n runId: string | null\n progressJobId: string | null\n}\n\nconst ENTITY_TYPE = 'customers.person' as const\nconst SESSION_STORAGE_PREFIX = 'om:sync_excel:session'\nconst RUN_POLL_INTERVAL_MS = 4_000\n\nconst SELECT_CLASS_NAME = 'flex h-9 w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'\n\nfunction buildMappingRows(headers: string[], suggestedMapping: SuggestedMapping): MappingRowState[] {\n const mappedFields = new Map(suggestedMapping.fields.map((field) => [field.externalField, field.localField]))\n return headers.map((header) => ({\n sourceColumn: header,\n targetField: mappedFields.get(header) ?? '',\n }))\n}\n\nfunction buildFieldMapping(sourceColumn: string, targetField: string, targetOptions: MappingTargetOption[]): FieldMapping {\n const matchedOption = findMappingTargetOption(targetOptions, targetField)\n const mappingKind = matchedOption?.mappingKind ?? (targetField.startsWith('cf:') ? 'custom_field' : 'core')\n const dedupeRole = matchedOption?.dedupeRole\n\n return {\n externalField: sourceColumn,\n localField: targetField,\n mappingKind,\n ...(dedupeRole ? { dedupeRole } : {}),\n }\n}\n\nfunction inferMatchStrategy(rows: MappingRowState[]): SuggestedMapping['matchStrategy'] {\n if (rows.some((row) => row.targetField === 'person.externalId')) return 'externalId'\n if (rows.some((row) => row.targetField === 'person.primaryEmail')) return 'email'\n return 'custom'\n}\n\nfunction buildMapping(\n rows: MappingRowState[],\n matchStrategy: SuggestedMapping['matchStrategy'],\n targetOptions: MappingTargetOption[],\n): SuggestedMapping {\n const mappedRows = rows.filter((row) => row.targetField.trim().length > 0)\n const fields = mappedRows.map((row) => buildFieldMapping(row.sourceColumn, row.targetField, targetOptions))\n const resolvedMatchStrategy = matchStrategy === 'custom' ? inferMatchStrategy(rows) : matchStrategy\n const matchField = resolvedMatchStrategy === 'externalId'\n ? 'person.externalId'\n : resolvedMatchStrategy === 'email'\n ? 'person.primaryEmail'\n : undefined\n\n return {\n entityType: ENTITY_TYPE,\n matchStrategy: resolvedMatchStrategy,\n ...(matchField ? { matchField } : {}),\n fields,\n unmappedColumns: rows.filter((row) => row.targetField.trim().length === 0).map((row) => row.sourceColumn),\n }\n}\n\nfunction buildDiagnostics(rows: MappingRowState[]): MappingDiagnostics {\n const duplicateTargets = Array.from(\n rows\n .filter((row) => row.targetField.trim().length > 0)\n .reduce((accumulator, row) => {\n const nextCount = (accumulator.get(row.targetField) ?? 0) + 1\n accumulator.set(row.targetField, nextCount)\n return accumulator\n }, new Map<string, number>())\n .entries(),\n )\n .filter(([, count]) => count > 1)\n .map(([target]) => target)\n\n const hasNameTarget = rows.some((row) => row.targetField === 'person.lastName' || row.targetField === 'person.displayName')\n const hasIdentityTarget = rows.some((row) => row.targetField === 'person.externalId' || row.targetField === 'person.primaryEmail')\n const mappedCount = rows.filter((row) => row.targetField.trim().length > 0).length\n\n return {\n duplicateTargets,\n hasNameTarget,\n hasIdentityTarget,\n mappedCount,\n unmappedCount: rows.length - mappedCount,\n }\n}\n\nfunction formatTargetLabel(\n targetField: string,\n t: ReturnType<typeof useT>,\n targetOptions: MappingTargetOption[],\n): string {\n const option = findMappingTargetOption(targetOptions, targetField)\n if (!option) return targetField\n return option.labelKey ? t(option.labelKey, option.fallback) : option.fallback\n}\n\nfunction getMatchStrategyLabel(\n matchStrategy: SuggestedMapping['matchStrategy'],\n t: ReturnType<typeof useT>,\n): string {\n if (matchStrategy === 'externalId') {\n return t('sync_excel.widget.matchStrategy.externalId', 'Source record ID (Recommended)')\n }\n if (matchStrategy === 'email') {\n return t('sync_excel.widget.matchStrategy.email', 'Email address')\n }\n return t('sync_excel.widget.matchStrategy.custom', 'Custom matching')\n}\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction readApiErrorMessage(result: Record<string, unknown> | null | undefined, fallback: string): string {\n const value = result?.error\n if (typeof value === 'string' && value.trim().length > 0) return value\n return fallback\n}\n\nfunction normalizeStatus(status: SyncRunDetail['status'] | ImportResponse['status'] | null | undefined): 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'idle' {\n if (status === 'pending' || status === 'running' || status === 'completed' || status === 'failed' || status === 'cancelled') {\n return status\n }\n return 'idle'\n}\n\nfunction getSessionStorageKey(integrationId: string | undefined): string {\n return `${SESSION_STORAGE_PREFIX}:${integrationId ?? 'sync_excel'}`\n}\n\nfunction readPersistedSessionSnapshot(integrationId: string | undefined): PersistedSessionSnapshot | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.sessionStorage.getItem(getSessionStorageKey(integrationId))\n if (!raw) return null\n const parsed = JSON.parse(raw) as Partial<PersistedSessionSnapshot> | null\n if (!parsed || typeof parsed !== 'object') return null\n if (typeof parsed.uploadId !== 'string' || parsed.uploadId.trim().length === 0) return null\n if (typeof parsed.filename !== 'string') return null\n if (!Array.isArray(parsed.mappingRows)) return null\n if (parsed.matchStrategy !== 'externalId' && parsed.matchStrategy !== 'email' && parsed.matchStrategy !== 'custom') {\n return null\n }\n const preview = (\n parsed.preview\n && typeof parsed.preview === 'object'\n && typeof parsed.preview.uploadId === 'string'\n && parsed.preview.uploadId.trim().length > 0\n && typeof parsed.preview.filename === 'string'\n && Array.isArray(parsed.preview.headers)\n && Array.isArray(parsed.preview.sampleRows)\n && typeof parsed.preview.totalRows === 'number'\n && typeof parsed.preview.fileSize === 'number'\n && parsed.preview.entityType === ENTITY_TYPE\n && parsed.preview.suggestedMapping\n )\n ? parsed.preview as UploadResponse\n : null\n\n return {\n uploadId: parsed.uploadId,\n filename: parsed.filename,\n preview,\n mappingRows: parsed.mappingRows\n .filter((row): row is MappingRowState => (\n Boolean(row)\n && typeof row.sourceColumn === 'string'\n && typeof row.targetField === 'string'\n )),\n matchStrategy: parsed.matchStrategy,\n runId: typeof parsed.runId === 'string' && parsed.runId.trim().length > 0 ? parsed.runId : null,\n progressJobId: typeof parsed.progressJobId === 'string' && parsed.progressJobId.trim().length > 0 ? parsed.progressJobId : null,\n }\n } catch {\n return null\n }\n}\n\nfunction writePersistedSessionSnapshot(\n integrationId: string | undefined,\n snapshot: PersistedSessionSnapshot | null,\n): void {\n if (typeof window === 'undefined') return\n try {\n const key = getSessionStorageKey(integrationId)\n if (!snapshot) {\n window.sessionStorage.removeItem(key)\n return\n }\n window.sessionStorage.setItem(key, JSON.stringify(snapshot))\n } catch {\n // sessionStorage can be unavailable in private/locked-down browser contexts; persistence is best-effort.\n }\n}\n\nexport default function SyncExcelUploadConfigWidget({\n context,\n data,\n}: InjectionWidgetComponentProps<SyncExcelIntegrationContext, SyncExcelIntegrationData>) {\n const pathname = usePathname()\n const router = useRouter()\n const searchParams = useSearchParams()\n const t = useT()\n const { data: customFieldDefs = [] } = useCustomFieldDefs([...SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS])\n const [selectedFile, setSelectedFile] = React.useState<File | null>(null)\n const [preview, setPreview] = React.useState<UploadResponse | null>(null)\n const [mappingRows, setMappingRows] = React.useState<MappingRowState[]>([])\n const [matchStrategy, setMatchStrategy] = React.useState<SuggestedMapping['matchStrategy']>('custom')\n const [isMappingDirty, setIsMappingDirty] = React.useState(false)\n const [runDetail, setRunDetail] = React.useState<SyncRunDetail | null>(null)\n const [runId, setRunId] = React.useState<string | null>(null)\n const [progressJobId, setProgressJobId] = React.useState<string | null>(null)\n const [isUploading, setIsUploading] = React.useState(false)\n const [isRefreshingPreview, setIsRefreshingPreview] = React.useState(false)\n const [isStartingImport, setIsStartingImport] = React.useState(false)\n const [isCancelling, setIsCancelling] = React.useState(false)\n const [isRestoringSession, setIsRestoringSession] = React.useState(true)\n const mutationContextId = `${context.formId ?? 'sync-excel'}:sync-excel-import`\n const lastAppliedSuggestionSignatureRef = React.useRef<string | null>(null)\n const restoringSnapshotRef = React.useRef<PersistedSessionSnapshot | null>(null)\n const lastRestoreRequestKeyRef = React.useRef<string | null>(null)\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n spotId: context.integrationDetailWidgetSpotId,\n })\n\n const targetOptions = React.useMemo(\n () => buildPeopleTargetOptions(customFieldDefs),\n [customFieldDefs],\n )\n const previewSuggestion = React.useMemo(\n () => preview ? buildPeopleSuggestedMapping(preview.headers, preview.suggestedMapping, customFieldDefs) : null,\n [customFieldDefs, preview],\n )\n const resolvedIntegrationEnabled = data?.state?.isEnabled ?? context.state?.isEnabled ?? true\n const diagnostics = React.useMemo(() => buildDiagnostics(mappingRows), [mappingRows])\n const canStartImport = Boolean(preview)\n && diagnostics.duplicateTargets.length === 0\n && diagnostics.hasNameTarget\n && diagnostics.hasIdentityTarget\n && mappingRows.some((row) => row.targetField.trim().length > 0)\n const importStatus = normalizeStatus(runDetail?.status ?? (runId ? 'pending' : null))\n const progressValue = runDetail?.progressJob?.progressPercent ?? 0\n const isRunActive = importStatus === 'pending' || importStatus === 'running'\n const searchParamsKey = searchParams?.toString() ?? ''\n const uploadIdFromUrl = searchParams?.get('uploadId') ?? null\n const runIdFromUrl = searchParams?.get('runId') ?? null\n const shouldHideInteractiveContent = isRestoringSession && Boolean(uploadIdFromUrl)\n const hasExistingRunForCurrentUpload = Boolean(preview?.uploadId && runId)\n const hasSafeDedupeStrategy = matchStrategy === 'externalId' || matchStrategy === 'email'\n\n const replaceQueryParams = React.useCallback((updates: Record<string, string | null | undefined>) => {\n const params = new URLSearchParams(searchParamsKey)\n for (const [key, value] of Object.entries(updates)) {\n if (typeof value === 'string' && value.trim().length > 0) {\n params.set(key, value)\n } else {\n params.delete(key)\n }\n }\n const query = params.toString()\n if (query === searchParamsKey) return\n router.replace(query ? `${pathname}?${query}` : pathname)\n }, [pathname, router, searchParamsKey])\n\n const clearPersistedSession = React.useCallback(() => {\n writePersistedSessionSnapshot(context.integrationId, null)\n replaceQueryParams({ uploadId: null, runId: null })\n }, [context.integrationId, replaceQueryParams])\n\n const persistCurrentSession = React.useCallback((next?: Partial<PersistedSessionSnapshot>) => {\n const persistedPreview = next?.preview ?? preview ?? null\n const uploadId = next?.uploadId ?? persistedPreview?.uploadId ?? null\n const filename = next?.filename ?? persistedPreview?.filename ?? null\n if (!uploadId || !filename) {\n writePersistedSessionSnapshot(context.integrationId, null)\n return\n }\n\n writePersistedSessionSnapshot(context.integrationId, {\n uploadId,\n filename,\n preview: persistedPreview,\n mappingRows: next?.mappingRows ?? mappingRows,\n matchStrategy: next?.matchStrategy ?? matchStrategy,\n runId: next?.runId ?? runId,\n progressJobId: next?.progressJobId ?? progressJobId,\n })\n }, [context.integrationId, mappingRows, matchStrategy, preview, progressJobId, runId])\n\n const syncPreviewState = React.useCallback((nextPreview: UploadResponse, options?: {\n preserveManualState?: boolean\n restoredSnapshot?: PersistedSessionSnapshot | null\n }) => {\n const nextSuggestedMapping = buildPeopleSuggestedMapping(nextPreview.headers, nextPreview.suggestedMapping, customFieldDefs)\n setPreview(nextPreview)\n\n const restoredSnapshot = options?.restoredSnapshot\n const shouldRestoreSnapshot = Boolean(\n restoredSnapshot\n && restoredSnapshot.uploadId === nextPreview.uploadId\n && restoredSnapshot.mappingRows.length > 0,\n )\n\n if (shouldRestoreSnapshot) {\n setMappingRows(restoredSnapshot!.mappingRows)\n setMatchStrategy(restoredSnapshot!.matchStrategy)\n setRunId(restoredSnapshot!.runId)\n setProgressJobId(restoredSnapshot!.progressJobId)\n setIsMappingDirty(true)\n lastAppliedSuggestionSignatureRef.current = null\n persistCurrentSession({\n uploadId: nextPreview.uploadId,\n filename: nextPreview.filename,\n preview: nextPreview,\n mappingRows: restoredSnapshot!.mappingRows,\n matchStrategy: restoredSnapshot!.matchStrategy,\n runId: restoredSnapshot!.runId,\n progressJobId: restoredSnapshot!.progressJobId,\n })\n replaceQueryParams({\n uploadId: nextPreview.uploadId,\n runId: restoredSnapshot!.runId,\n })\n return\n }\n\n if (options?.preserveManualState && preview?.uploadId === nextPreview.uploadId) {\n persistCurrentSession({\n uploadId: nextPreview.uploadId,\n filename: nextPreview.filename,\n preview: nextPreview,\n })\n replaceQueryParams({ uploadId: nextPreview.uploadId })\n return\n }\n\n const nextRows = buildMappingRows(nextPreview.headers, nextSuggestedMapping)\n setMappingRows(nextRows)\n setMatchStrategy(nextSuggestedMapping.matchStrategy)\n setIsMappingDirty(false)\n lastAppliedSuggestionSignatureRef.current = buildSuggestedMappingSignature(nextPreview.headers, nextSuggestedMapping)\n persistCurrentSession({\n uploadId: nextPreview.uploadId,\n filename: nextPreview.filename,\n preview: nextPreview,\n mappingRows: nextRows,\n matchStrategy: nextSuggestedMapping.matchStrategy,\n runId: null,\n progressJobId: null,\n })\n replaceQueryParams({\n uploadId: nextPreview.uploadId,\n runId: null,\n })\n }, [customFieldDefs, persistCurrentSession, preview?.uploadId, replaceQueryParams])\n\n React.useEffect(() => {\n if (!preview || !previewSuggestion) return\n if (isMappingDirty) return\n const nextSignature = buildSuggestedMappingSignature(preview.headers, previewSuggestion)\n if (lastAppliedSuggestionSignatureRef.current === nextSignature) return\n setMappingRows(buildMappingRows(preview.headers, previewSuggestion))\n setMatchStrategy(previewSuggestion.matchStrategy)\n lastAppliedSuggestionSignatureRef.current = nextSignature\n }, [isMappingDirty, preview, previewSuggestion])\n\n const refreshRunDetail = React.useCallback(async (currentRunId: string) => {\n const call = await apiCall<SyncRunDetail>(`/api/data_sync/runs/${encodeURIComponent(currentRunId)}`, undefined, { fallback: null })\n if (call.ok && call.result) {\n setRunDetail(call.result)\n setProgressJobId(call.result.progressJobId ?? null)\n persistCurrentSession({\n runId: currentRunId,\n progressJobId: call.result.progressJobId ?? null,\n })\n return call.result\n }\n return null\n }, [persistCurrentSession])\n\n React.useEffect(() => {\n if (!preview?.uploadId) return\n persistCurrentSession()\n }, [mappingRows, matchStrategy, persistCurrentSession, preview?.uploadId, progressJobId, runId])\n\n React.useEffect(() => {\n const persistedSnapshot = readPersistedSessionSnapshot(context.integrationId)\n const resolvedUploadId = uploadIdFromUrl ?? persistedSnapshot?.uploadId ?? null\n const resolvedRunId = runIdFromUrl ?? persistedSnapshot?.runId ?? null\n\n if (!resolvedUploadId) {\n restoringSnapshotRef.current = null\n lastRestoreRequestKeyRef.current = null\n setIsRestoringSession(false)\n return\n }\n\n if (!uploadIdFromUrl && persistedSnapshot?.uploadId === resolvedUploadId) {\n replaceQueryParams({\n uploadId: resolvedUploadId,\n runId: resolvedRunId,\n })\n }\n\n if (preview?.uploadId === resolvedUploadId && mappingRows.length > 0) {\n if (resolvedRunId && runId !== resolvedRunId) {\n setRunId(resolvedRunId)\n }\n setIsRestoringSession(false)\n return\n }\n\n const restoreRequestKey = `${resolvedUploadId}:${resolvedRunId ?? ''}`\n if (lastRestoreRequestKeyRef.current === restoreRequestKey) {\n setIsRestoringSession(false)\n return\n }\n\n lastRestoreRequestKeyRef.current = restoreRequestKey\n\n let cancelled = false\n restoringSnapshotRef.current = persistedSnapshot\n const persistedPreview = restoringSnapshotRef.current?.preview?.uploadId === resolvedUploadId\n ? restoringSnapshotRef.current.preview\n : null\n\n if (persistedPreview) {\n syncPreviewState(persistedPreview, {\n restoredSnapshot: restoringSnapshotRef.current,\n })\n setIsRestoringSession(false)\n } else {\n setIsRestoringSession(true)\n }\n\n const restoreSession = async () => {\n const call = await apiCall<UploadResponse>(\n `/api/sync_excel/preview?uploadId=${encodeURIComponent(resolvedUploadId)}&entityType=${encodeURIComponent(ENTITY_TYPE)}`,\n undefined,\n { fallback: null },\n )\n\n if (cancelled) return\n\n if (!call.ok || !call.result) {\n setPreview(null)\n setMappingRows([])\n setMatchStrategy('custom')\n setRunId(null)\n setRunDetail(null)\n setProgressJobId(null)\n clearPersistedSession()\n lastRestoreRequestKeyRef.current = null\n setIsRestoringSession(false)\n return\n }\n\n syncPreviewState(call.result, {\n restoredSnapshot: restoringSnapshotRef.current,\n })\n\n if (resolvedRunId && !(restoringSnapshotRef.current?.runId)) {\n setRunId(resolvedRunId)\n }\n\n setIsRestoringSession(false)\n }\n\n void restoreSession()\n\n return () => {\n cancelled = true\n }\n }, [clearPersistedSession, context.integrationId, mappingRows.length, preview?.uploadId, replaceQueryParams, runId, runIdFromUrl, syncPreviewState, uploadIdFromUrl])\n\n React.useEffect(() => {\n if (!runId) return\n\n let cancelled = false\n let timeoutId: ReturnType<typeof setTimeout> | null = null\n\n const poll = async () => {\n const detail = await refreshRunDetail(runId)\n if (cancelled) return\n if (context.activeTab === 'logs') {\n await context.refreshLogs?.()\n }\n if (context.activeTab === 'health' || normalizeStatus(detail?.status) !== 'idle') {\n await context.refreshHealthSnapshot?.()\n }\n const status = normalizeStatus(detail?.status)\n if (status === 'pending' || status === 'running') {\n timeoutId = setTimeout(() => {\n void poll()\n }, RUN_POLL_INTERVAL_MS)\n }\n }\n\n void poll()\n\n return () => {\n cancelled = true\n if (timeoutId) clearTimeout(timeoutId)\n }\n }, [context.activeTab, context.refreshHealthSnapshot, context.refreshLogs, refreshRunDetail, runId])\n\n const handleUpload = React.useCallback(async () => {\n if (!selectedFile) {\n flash(t('sync_excel.widget.messages.fileRequired', 'Select a CSV file first.'), 'error')\n return\n }\n\n setIsUploading(true)\n try {\n const formData = new FormData()\n formData.set('file', selectedFile)\n formData.set('entityType', ENTITY_TYPE)\n\n const call = await runMutation({\n operation: () => apiCall<UploadResponse>('/api/sync_excel/upload', {\n method: 'POST',\n body: formData,\n }, { fallback: null }),\n mutationPayload: {\n integrationId: context.integrationId ?? 'sync_excel',\n entityType: ENTITY_TYPE,\n fileName: selectedFile.name,\n },\n context: {\n ...context,\n operation: 'create',\n actionId: 'sync-excel-upload',\n },\n })\n\n if (!call.ok || !call.result) {\n flash(\n readApiErrorMessage(call.result as Record<string, unknown> | null, t('sync_excel.widget.messages.uploadError', 'Failed to upload CSV file.')),\n 'error',\n )\n return\n }\n\n syncPreviewState(call.result)\n setSelectedFile(null)\n setRunId(null)\n setRunDetail(null)\n setProgressJobId(null)\n flash(t('sync_excel.widget.messages.uploadSuccess', 'CSV preview is ready.'), 'success')\n } catch {\n flash(t('sync_excel.widget.messages.uploadError', 'Failed to upload CSV file.'), 'error')\n } finally {\n setIsUploading(false)\n }\n }, [context, runMutation, selectedFile, syncPreviewState, t])\n\n const handleRefreshPreview = React.useCallback(async () => {\n if (!preview) return\n setIsRefreshingPreview(true)\n try {\n const call = await apiCall<UploadResponse>(\n `/api/sync_excel/preview?uploadId=${encodeURIComponent(preview.uploadId)}&entityType=${encodeURIComponent(ENTITY_TYPE)}`,\n undefined,\n { fallback: null },\n )\n if (!call.ok || !call.result) {\n flash(t('sync_excel.widget.messages.previewError', 'Failed to refresh preview.'), 'error')\n return\n }\n syncPreviewState(call.result, { preserveManualState: true })\n flash(t('sync_excel.widget.messages.previewRefreshed', 'Preview refreshed.'), 'success')\n } catch {\n flash(t('sync_excel.widget.messages.previewError', 'Failed to refresh preview.'), 'error')\n } finally {\n setIsRefreshingPreview(false)\n }\n }, [preview, syncPreviewState, t])\n\n const handleTargetFieldChange = React.useCallback((sourceColumn: string, targetField: string) => {\n setIsMappingDirty(true)\n setMappingRows((current) => current.map((row) => row.sourceColumn === sourceColumn ? { ...row, targetField } : row))\n }, [])\n\n const handleResetToSuggested = React.useCallback(() => {\n if (!preview || !previewSuggestion) return\n setMappingRows(buildMappingRows(preview.headers, previewSuggestion))\n setMatchStrategy(previewSuggestion.matchStrategy)\n setIsMappingDirty(false)\n lastAppliedSuggestionSignatureRef.current = buildSuggestedMappingSignature(preview.headers, previewSuggestion)\n flash(t('sync_excel.widget.messages.mappingReset', 'Suggested mapping restored.'), 'success')\n }, [preview, previewSuggestion, t])\n\n const handleStartImport = React.useCallback(async () => {\n if (!preview) return\n\n const mapping = buildMapping(mappingRows, matchStrategy, targetOptions)\n setIsStartingImport(true)\n\n try {\n const call = await runMutation({\n operation: () => apiCall<ImportResponse>('/api/sync_excel/import', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n uploadId: preview.uploadId,\n entityType: ENTITY_TYPE,\n mapping,\n }),\n }, { fallback: null }),\n mutationPayload: {\n integrationId: context.integrationId ?? 'sync_excel',\n uploadId: preview.uploadId,\n entityType: ENTITY_TYPE,\n mapping,\n },\n context: {\n ...context,\n operation: 'create',\n actionId: 'sync-excel-start-import',\n },\n })\n\n if (!call.ok || !call.result) {\n flash(\n readApiErrorMessage(call.result as Record<string, unknown> | null, t('sync_excel.widget.messages.importError', 'Failed to start import run.')),\n 'error',\n )\n return\n }\n\n setRunId(call.result.runId)\n setProgressJobId(call.result.progressJobId)\n replaceQueryParams({\n uploadId: preview.uploadId,\n runId: call.result.runId,\n })\n persistCurrentSession({\n uploadId: preview.uploadId,\n filename: preview.filename,\n runId: call.result.runId,\n progressJobId: call.result.progressJobId,\n })\n flash(t('sync_excel.widget.messages.importStarted', 'Import run started.'), 'success')\n await refreshRunDetail(call.result.runId)\n await context.refreshLogs?.()\n await context.refreshHealthSnapshot?.()\n } catch {\n flash(t('sync_excel.widget.messages.importError', 'Failed to start import run.'), 'error')\n } finally {\n setIsStartingImport(false)\n }\n }, [context, mappingRows, matchStrategy, persistCurrentSession, preview, refreshRunDetail, replaceQueryParams, runMutation, t, targetOptions])\n\n const handleCancelRun = React.useCallback(async () => {\n if (!runId) return\n\n setIsCancelling(true)\n try {\n const call = await runMutation({\n operation: () => apiCall<{ ok?: boolean }>(`/api/data_sync/runs/${encodeURIComponent(runId)}/cancel`, {\n method: 'POST',\n }, { fallback: null }),\n mutationPayload: {\n integrationId: context.integrationId ?? 'sync_excel',\n runId,\n },\n context: {\n ...context,\n operation: 'update',\n actionId: 'sync-excel-cancel-import',\n },\n })\n\n if (!call.ok) {\n flash(t('sync_excel.widget.messages.cancelError', 'Failed to cancel import run.'), 'error')\n return\n }\n\n flash(t('sync_excel.widget.messages.cancelSuccess', 'Import run cancelled.'), 'success')\n await refreshRunDetail(runId)\n await context.refreshLogs?.()\n await context.refreshHealthSnapshot?.()\n } catch {\n flash(t('sync_excel.widget.messages.cancelError', 'Failed to cancel import run.'), 'error')\n } finally {\n setIsCancelling(false)\n }\n }, [context, refreshRunDetail, runId, runMutation, t])\n\n const handleRefreshAll = React.useCallback(async () => {\n if (!runId) return\n await refreshRunDetail(runId)\n await context.refreshLogs?.()\n await context.refreshHealthSnapshot?.()\n }, [context, refreshRunDetail, runId])\n\n const firstSampleRow = preview?.sampleRows[0] ?? null\n\n return (\n <div className=\"space-y-6\">\n {isRestoringSession ? (\n <Card>\n <CardContent className=\"flex items-center gap-3 py-6\">\n <Spinner className=\"size-4\" />\n <div className=\"text-sm text-muted-foreground\">\n {t('sync_excel.widget.messages.restoringSession', 'Restoring the last CSV session...')}\n </div>\n </CardContent>\n </Card>\n ) : null}\n\n {!resolvedIntegrationEnabled ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.disabled.title', 'Integration disabled')}\n message={t('sync_excel.widget.disabled.message', 'Enable this integration to upload CSV files and start imports.')}\n />\n ) : null}\n\n {!shouldHideInteractiveContent ? (\n <Card>\n <CardHeader>\n <CardTitle>{t('sync_excel.widget.upload.title', 'Upload CSV')}</CardTitle>\n <CardDescription>\n {t('sync_excel.widget.upload.description', 'Upload a CSV file, review the suggested people mapping, and start a background import run.')}\n </CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"sync-excel-upload-file\">{t('sync_excel.widget.upload.fileLabel', 'CSV file')}</Label>\n <Input\n id=\"sync-excel-upload-file\"\n type=\"file\"\n accept=\".csv,text/csv\"\n onChange={(event) => setSelectedFile(event.target.files?.[0] ?? null)}\n disabled={isUploading || isRunActive || isRestoringSession}\n />\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <Button type=\"button\" onClick={() => void handleUpload()} disabled={!selectedFile || isUploading || isRunActive || isRestoringSession}>\n {isUploading ? <Spinner className=\"mr-2 size-4\" /> : <Upload className=\"mr-2 size-4\" />}\n {isUploading\n ? t('sync_excel.widget.actions.uploadPending', 'Uploading...')\n : t('sync_excel.widget.actions.upload', 'Upload and preview')}\n </Button>\n {preview ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => void handleRefreshPreview()} disabled={isRefreshingPreview || isUploading || isRunActive || isRestoringSession}>\n {isRefreshingPreview ? <Spinner className=\"mr-2 size-4\" /> : <RefreshCw className=\"mr-2 size-4\" />}\n {isRefreshingPreview\n ? t('sync_excel.widget.actions.refreshPending', 'Refreshing...')\n : t('sync_excel.widget.actions.refresh', 'Refresh preview')}\n </Button>\n ) : null}\n </div>\n\n {selectedFile ? (\n <div className=\"text-sm text-muted-foreground\">\n {t('sync_excel.widget.upload.selectedFile', 'Selected file')}: <span className=\"font-medium text-foreground\">{selectedFile.name}</span>\n </div>\n ) : null}\n </CardContent>\n </Card>\n ) : null}\n\n {preview && !shouldHideInteractiveContent ? (\n <Card>\n <CardHeader>\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <CardTitle>{t('sync_excel.widget.preview.title', 'Preview and mapping')}</CardTitle>\n <CardDescription>\n {t('sync_excel.widget.preview.description', 'Every source column stays visible here. Leave a target empty to keep the column unmapped for now.')}\n </CardDescription>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Badge variant=\"outline\">{preview.filename}</Badge>\n <Badge variant=\"outline\">{t('sync_excel.widget.preview.rows', '{count} rows', { count: preview.totalRows })}</Badge>\n <Badge variant=\"outline\">{formatFileSize(preview.fileSize)}</Badge>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n <MetricCard\n label={t('sync_excel.widget.metrics.mapped', 'Mapped columns')}\n value={String(diagnostics.mappedCount)}\n />\n <MetricCard\n label={t('sync_excel.widget.metrics.unmapped', 'Unmapped columns')}\n value={String(diagnostics.unmappedCount)}\n />\n <MetricCard\n label={t('sync_excel.widget.metrics.identity', 'Identity strategy')}\n value={getMatchStrategyLabel(matchStrategy, t)}\n />\n <MetricCard\n label={t('sync_excel.widget.metrics.entity', 'Target entity')}\n value={ENTITY_TYPE}\n />\n </div>\n\n <div className=\"grid gap-4 lg:grid-cols-[minmax(0,1fr)_280px]\">\n <div className=\"overflow-hidden rounded-lg border\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>{t('sync_excel.widget.mapping.sourceColumn', 'Source column')}</TableHead>\n <TableHead>{t('sync_excel.widget.mapping.sampleValue', 'Sample value')}</TableHead>\n <TableHead>{t('sync_excel.widget.mapping.targetField', 'Target field')}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {mappingRows.map((row) => (\n <TableRow key={row.sourceColumn}>\n <TableCell className=\"font-medium\">{row.sourceColumn}</TableCell>\n <TableCell className=\"max-w-[260px] truncate text-muted-foreground\">\n {firstSampleRow?.[row.sourceColumn] ?? '\u2014'}\n </TableCell>\n <TableCell>\n <select\n className={SELECT_CLASS_NAME}\n value={row.targetField}\n onChange={(event) => handleTargetFieldChange(row.sourceColumn, event.target.value)}\n disabled={isStartingImport || isRunActive}\n >\n <option value=\"\">{t('sync_excel.mapping.targets.ignore', 'Leave unmapped')}</option>\n {targetOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.labelKey ? t(option.labelKey, option.fallback) : option.fallback}\n </option>\n ))}\n </select>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"sync-excel-match-strategy\">\n {t('sync_excel.widget.matchStrategy.label', 'How to match existing people')}\n </Label>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'sync_excel.widget.matchStrategy.help',\n 'This decides whether imported rows update existing people or create new ones.',\n )}\n </p>\n <select\n id=\"sync-excel-match-strategy\"\n className={SELECT_CLASS_NAME}\n value={matchStrategy}\n onChange={(event) => {\n setIsMappingDirty(true)\n setMatchStrategy(event.target.value as SuggestedMapping['matchStrategy'])\n }}\n disabled={isStartingImport || isRunActive}\n >\n <option value=\"externalId\">\n {t('sync_excel.widget.matchStrategy.externalId', 'Source record ID (Recommended)')}\n </option>\n <option value=\"email\">{t('sync_excel.widget.matchStrategy.email', 'Email address')}</option>\n <option value=\"custom\">{t('sync_excel.widget.matchStrategy.custom', 'Custom matching')}</option>\n </select>\n <p className=\"text-sm text-muted-foreground\">\n {matchStrategy === 'externalId'\n ? t(\n 'sync_excel.widget.matchStrategy.externalIdDescription',\n 'Best for repeated imports from the same source. Future imports update the same people instead of creating duplicates.',\n )\n : matchStrategy === 'email'\n ? t(\n 'sync_excel.widget.matchStrategy.emailDescription',\n 'Matches people by email. Works best when each person has one reliable, unique email address.',\n )\n : t(\n 'sync_excel.widget.matchStrategy.customDescription',\n 'Advanced option. Use only if you know how existing records should be identified in this import.',\n )}\n </p>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={handleResetToSuggested} disabled={isStartingImport || isRunActive}>\n <RefreshCw className=\"mr-2 size-4\" />\n {t('sync_excel.widget.actions.reset', 'Reset to suggestion')}\n </Button>\n <Button type=\"button\" onClick={() => void handleStartImport()} disabled={!canStartImport || isStartingImport || isRunActive}>\n {isStartingImport ? <Spinner className=\"mr-2 size-4\" /> : <Play className=\"mr-2 size-4\" />}\n {isStartingImport\n ? t('sync_excel.widget.actions.startPending', 'Starting...')\n : t('sync_excel.widget.actions.start', 'Start import')}\n </Button>\n </div>\n\n {matchStrategy === 'custom' ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.matchStrategy.customWarningTitle', 'Use custom matching with care')}\n message={t(\n 'sync_excel.widget.matchStrategy.customWarningMessage',\n 'Custom matching may create duplicates if the mapped fields do not uniquely identify a person.',\n )}\n />\n ) : null}\n\n {hasExistingRunForCurrentUpload ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.reimportTitle', 'This upload already has a run')}\n message={t(\n 'sync_excel.widget.validation.reimportMessage',\n 'Starting another import from the same uploaded CSV may create duplicates unless your matching strategy reliably identifies existing people.',\n )}\n />\n ) : null}\n\n {!hasSafeDedupeStrategy ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.duplicateRiskTitle', 'Duplicate risk')}\n message={t(\n 'sync_excel.widget.validation.duplicateRiskMessage',\n 'This mapping does not use the recommended External ID or Email matching strategy. Re-importing the same dataset may create duplicate people.',\n )}\n />\n ) : null}\n\n {diagnostics.duplicateTargets.length > 0 ? (\n <StatusAlert\n variant=\"error\"\n title={t('sync_excel.widget.validation.duplicateTargetsTitle', 'Duplicate target fields')}\n message={t(\n 'sync_excel.widget.validation.duplicateTargetsMessage',\n 'Each target field can only be mapped once in this foundation slice: {targets}',\n { targets: diagnostics.duplicateTargets.map((target) => formatTargetLabel(target, t, targetOptions)).join(', ') },\n )}\n />\n ) : null}\n\n {!diagnostics.hasIdentityTarget ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.identityTitle', 'Map an identity field')}\n message={t('sync_excel.widget.validation.identityMessage', 'Map either External ID or Primary email so reimports can match existing people.')}\n />\n ) : null}\n\n {!diagnostics.hasNameTarget ? (\n <StatusAlert\n variant=\"warning\"\n title={t('sync_excel.widget.validation.nameTitle', 'Map a name field')}\n message={t('sync_excel.widget.validation.nameMessage', 'Map Last name or Display name so the importer can build person records.')}\n />\n ) : null}\n\n {diagnostics.hasIdentityTarget && diagnostics.hasNameTarget && diagnostics.duplicateTargets.length === 0 ? (\n <StatusAlert\n variant=\"info\"\n title={t('sync_excel.widget.validation.readyTitle', 'Ready to import')}\n message={t('sync_excel.widget.validation.readyMessage', 'The current mapping is valid for the first customers.person slice.')}\n />\n ) : null}\n </div>\n </div>\n </CardContent>\n </Card>\n ) : null}\n\n {runId && !shouldHideInteractiveContent ? (\n <Card>\n <CardHeader>\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <CardTitle>{t('sync_excel.widget.run.title', 'Import run status')}</CardTitle>\n <CardDescription>\n {t('sync_excel.widget.run.description', 'The import runs in the background through Data Sync and updates this view as progress changes.')}\n </CardDescription>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <RunStatusBadge status={importStatus} t={t} />\n {progressJobId ? <Badge variant=\"outline\">{progressJobId}</Badge> : null}\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-muted-foreground\">{t('sync_excel.widget.run.progressLabel', 'Progress')}</span>\n <span className=\"font-medium\">{progressValue}%</span>\n </div>\n <Progress value={progressValue} className=\"h-2.5\" />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-5\">\n <MetricCard label={t('sync_excel.widget.run.metrics.created', 'Created')} value={String(runDetail?.createdCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.updated', 'Updated')} value={String(runDetail?.updatedCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.skipped', 'Skipped')} value={String(runDetail?.skippedCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.failed', 'Failed')} value={String(runDetail?.failedCount ?? 0)} />\n <MetricCard label={t('sync_excel.widget.run.metrics.processed', 'Processed')} value={String(runDetail?.progressJob?.processedCount ?? 0)} />\n </div>\n\n {runDetail?.lastError ? (\n <StatusAlert\n variant=\"error\"\n title={t('sync_excel.widget.run.errorTitle', 'Run error')}\n message={runDetail.lastError}\n />\n ) : null}\n\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" variant=\"outline\" onClick={() => void handleRefreshAll()} disabled={isRefreshingPreview || isRestoringSession}>\n <RefreshCw className=\"mr-2 size-4\" />\n {t('sync_excel.widget.actions.refreshRun', 'Refresh run status')}\n </Button>\n {isRunActive ? (\n <Button type=\"button\" variant=\"destructive\" onClick={() => void handleCancelRun()} disabled={isCancelling}>\n {isCancelling ? <Spinner className=\"mr-2 size-4\" /> : <XCircle className=\"mr-2 size-4\" />}\n {isCancelling\n ? t('sync_excel.widget.actions.cancelPending', 'Cancelling...')\n : t('sync_excel.widget.actions.cancel', 'Cancel run')}\n </Button>\n ) : null}\n </div>\n </CardContent>\n </Card>\n ) : null}\n </div>\n )\n}\n\nfunction StatusAlert({\n variant,\n title,\n message,\n}: {\n variant: 'warning' | 'error' | 'info'\n title: string\n message: string\n}) {\n return (\n <Alert status={variant === 'info' ? 'information' : variant}>\n <AlertTitle>{title}</AlertTitle>\n <AlertDescription>{message}</AlertDescription>\n </Alert>\n )\n}\n\nfunction MetricCard({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"rounded-lg border bg-muted/30 p-4\">\n <div className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">{label}</div>\n <div className=\"mt-2 text-lg font-semibold text-foreground\">{value}</div>\n </div>\n )\n}\n\ntype RunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'idle'\n\nexport const RUN_STATUS_BADGE_VARIANTS: StatusMap<RunStatus> = {\n completed: 'success',\n failed: 'error',\n cancelled: 'neutral',\n pending: 'info',\n running: 'info',\n idle: 'neutral',\n}\n\nexport function RunStatusBadge({\n status,\n t,\n}: {\n status: RunStatus\n t: ReturnType<typeof useT>\n}) {\n const variant = RUN_STATUS_BADGE_VARIANTS[status] ?? 'neutral'\n\n if (status === 'completed') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <CheckCircle2 className=\"size-3.5\" />\n {t('sync_excel.widget.run.status.completed', 'Completed')}\n </StatusBadge>\n )\n }\n\n if (status === 'failed') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <AlertTriangle className=\"size-3.5\" />\n {t('sync_excel.widget.run.status.failed', 'Failed')}\n </StatusBadge>\n )\n }\n\n if (status === 'cancelled') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <XCircle className=\"size-3.5\" />\n {t('sync_excel.widget.run.status.cancelled', 'Cancelled')}\n </StatusBadge>\n )\n }\n\n if (status === 'pending' || status === 'running') {\n return (\n <StatusBadge variant={variant} className=\"gap-1.5\">\n <RefreshCw className={cn('size-3.5', status === 'running' ? 'animate-spin' : '')} />\n {status === 'pending'\n ? t('sync_excel.widget.run.status.pending', 'Pending')\n : t('sync_excel.widget.run.status.running', 'Running')}\n </StatusBadge>\n )\n }\n\n return (\n <StatusBadge variant={variant}>\n {t('sync_excel.widget.run.status.idle', 'Idle')}\n </StatusBadge>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -228,11 +228,11 @@ function EditWorkflowDefinitionPage() {
|
|
|
228
228
|
}
|
|
229
229
|
return /* @__PURE__ */ jsxs(Page, { children: [
|
|
230
230
|
/* @__PURE__ */ jsxs(PageBody, { children: [
|
|
231
|
-
isCodeOnly && /* @__PURE__ */ jsx(Alert, {
|
|
231
|
+
isCodeOnly && /* @__PURE__ */ jsx(Alert, { status: "information", className: "mb-4", children: /* @__PURE__ */ jsxs(AlertDescription, { className: "flex items-center justify-between", children: [
|
|
232
232
|
/* @__PURE__ */ jsx("span", { children: t("workflows.source.code.readonlyBanner") }),
|
|
233
233
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleCustomize, children: t("workflows.actions.customize") })
|
|
234
234
|
] }) }),
|
|
235
|
-
isCodeOverride && /* @__PURE__ */ jsx(Alert, {
|
|
235
|
+
isCodeOverride && /* @__PURE__ */ jsx(Alert, { status: "warning", className: "mb-4", children: /* @__PURE__ */ jsxs(AlertDescription, { className: "flex items-center justify-between", children: [
|
|
236
236
|
/* @__PURE__ */ jsx("span", { children: t("workflows.source.code_override.banner") }),
|
|
237
237
|
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleResetToCode, children: t("workflows.actions.resetToCode") })
|
|
238
238
|
] }) }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/workflows/backend/definitions/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useParams, usePathname } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { apiFetch, withScopedApiHeaders } from '@open-mercato/ui/backend/utils/api'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { buildRecordInjectionContext, useSetCurrentRecordInjectionContext } from '@open-mercato/ui/backend/injection/recordContext'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { formatWorkflowValidationError } from '../../../lib/format-validation-error'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n parseWorkflowToFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { DefinitionTriggersEditor } from '../../../components/DefinitionTriggersEditor'\nimport { MobileDefinitionDetail } from '../../../components/mobile/MobileDefinitionDetail'\nimport { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'\nimport type { WorkflowDefinitionTrigger } from '../../../data/entities'\n\nexport default function EditWorkflowDefinitionPage() {\n const router = useRouter()\n const params = useParams()\n const pathname = usePathname()\n const t = useT()\n const isMobile = useIsMobile()\n\n // Handle catch-all route: params.slug = ['definitions', 'uuid']\n let definitionId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n definitionId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n definitionId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const { data: definition, isLoading, error } = useQuery({\n queryKey: ['workflow-definition', definitionId],\n queryFn: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`)\n if (!response.ok) {\n const err = new Error(t('workflows.errors.fetchFailed')) as Error & { status?: number }\n err.status = response.status\n throw err\n }\n const result = await response.json()\n return result.data\n },\n enabled: !!definitionId,\n retry: (failureCount, err) => {\n if ((err as { status?: number }).status === 404) return false\n return failureCount < 2\n },\n })\n\n const isNotFound = (error as { status?: number } | null)?.status === 404\n\n const initialValues = React.useMemo(() => {\n if (definition) {\n return parseWorkflowToFormValues(definition)\n }\n return null\n }, [definition])\n\n const [triggers, setTriggers] = React.useState<WorkflowDefinitionTrigger[]>([])\n\n React.useEffect(() => {\n setTriggers(initialValues?.triggers ?? [])\n }, [initialValues])\n\n const source = definition?.source as 'code' | 'code_override' | 'user' | undefined\n const isCodeOnly = source === 'code'\n const isCodeOverride = source === 'code_override'\n\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const mutationContextId = React.useMemo(\n () => `workflows.definitions.detail:${definitionId ?? 'unknown'}`,\n [definitionId],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n })\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload({ ...values, triggers })\n const optimisticLockHeader = buildOptimisticLockHeader(\n typeof definition?.updatedAt === 'string' ? definition.updatedAt : null,\n )\n\n await runMutation({\n operation: async () => {\n const updateDefinition = () => apiFetch(`/api/workflows/definitions/${definitionId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const response = Object.keys(optimisticLockHeader).length > 0\n ? await withScopedApiHeaders(optimisticLockHeader, updateDefinition)\n : await updateDefinition()\n if (!response.ok) {\n const errorBody = await readJsonSafe<{\n error?: string\n code?: string\n currentUpdatedAt?: string\n expectedUpdatedAt?: string\n details?: Array<{ path?: Array<string | number>; message?: string }>\n }>(response, null)\n throw Object.assign(\n new Error(formatWorkflowValidationError(errorBody, t('workflows.errors.updateFailed'))),\n { status: response.status, ...(errorBody ?? {}) },\n )\n }\n return response\n },\n mutationPayload: { resourceId: definitionId, operation: 'update' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'update',\n retryLastMutation,\n },\n })\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const handleCustomize = async () => {\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/customize`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.errors.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'customize' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'customize',\n retryLastMutation,\n },\n })\n if (result?.data?.id) {\n router.push(`/backend/definitions/${result.data.id}`)\n router.refresh()\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('workflows.errors.updateFailed')\n flash(message, 'error')\n }\n }\n\n const handleResetToCode = async () => {\n const confirmed = await confirm({\n title: t('workflows.actions.resetToCode'),\n description: t('workflows.actions.resetConfirm'),\n confirmText: t('workflows.actions.resetToCode'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/reset-to-code`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.messages.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'reset-to-code' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'reset-to-code',\n retryLastMutation,\n },\n })\n flash(t('workflows.messages.updated'), 'success')\n const codeId = result?.data?.id || `code:${definition?.workflowId}`\n router.push(`/backend/definitions/${codeId}`)\n router.refresh()\n } catch {\n flash(t('workflows.messages.updateFailed'), 'error')\n }\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => isMobile ? [] : createFormGroups(t, StepsEditor, TransitionsEditor),\n [t, isMobile]\n )\n\n const navigateToVisualEditor = React.useCallback(() => {\n router.push(`/backend/definitions/visual-editor?id=${definitionId}`)\n }, [router, definitionId])\n\n // Publish page-load record context to the AppShell-owned `backend:record:current`\n // mount so the enterprise record_locks widget resolves `workflows.definition` + id\n // explicitly. The resourceKind mirrors the save-time `useGuardedMutation` context\n // and server guard so the held lock matches the conflict surface for the same\n // definition across both the form and visual editors.\n useSetCurrentRecordInjectionContext(\n buildRecordInjectionContext({\n resourceKind: 'workflows.definition',\n resourceId: definitionId ?? null,\n updatedAt: typeof definition?.updatedAt === 'string' ? definition.updatedAt : null,\n data: (definition ?? null) as Record<string, unknown> | null,\n path: pathname,\n }),\n )\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <Spinner className=\"h-6 w-6\" />\n <span>{t('workflows.edit.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('workflows.errors.notFound', t('workflows.errors.loadFailed'))}\n backHref=\"/backend/definitions\"\n backLabel={t('workflows.backToList')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !definition) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={t('workflows.errors.loadFailed')} />\n <div className=\"mt-4 flex justify-center\">\n <Button asChild variant=\"outline\">\n <a href=\"/backend/definitions\">{t('workflows.backToList')}</a>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (!initialValues) {\n return null\n }\n\n return (\n <Page>\n <PageBody>\n {isCodeOnly && (\n <Alert variant=\"info\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code.readonlyBanner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCustomize}>\n {t('workflows.actions.customize')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n {isCodeOverride && (\n <Alert variant=\"warning\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code_override.banner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleResetToCode}>\n {t('workflows.actions.resetToCode')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n <div className=\"mb-4 p-4 bg-status-info-bg border border-status-info-border rounded-lg flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"w-5 h-5 text-status-info-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-info-text\">\n {t('workflows.edit.visualEditorAvailable')}\n </p>\n <p className=\"text-xs text-status-info-text mt-0.5\">\n {t('workflows.edit.visualEditorDescription')}\n </p>\n </div>\n </div>\n <Button asChild variant=\"outline\" size=\"sm\" className=\"w-full sm:w-auto border-status-info-border text-status-info-text hover:bg-status-info-bg\">\n <a href={`/backend/definitions/visual-editor?id=${definitionId}`}>\n {t('workflows.actions.openVisualEditor')}\n </a>\n </Button>\n </div>\n <CrudForm\n key={definitionId}\n title={isCodeOnly ? definition?.workflowName || t('workflows.edit.title') : t('workflows.edit.title')}\n backHref=\"/backend/definitions\"\n schema={workflowDefinitionFormSchema}\n fields={fields}\n initialValues={initialValues}\n onSubmit={handleSubmit}\n cancelHref=\"/backend/definitions\"\n groups={formGroups}\n submitLabel={isCodeOnly ? t('workflows.actions.customize') : t('workflows.form.update')}\n {...(isCodeOnly ? { readOnly: true } : {})}\n />\n\n {/* Mobile Steps & Transitions View */}\n {isMobile && initialValues && (\n <div className=\"mt-4\">\n <MobileDefinitionDetail\n values={initialValues}\n onEditStep={navigateToVisualEditor}\n onDeleteStep={navigateToVisualEditor}\n onAddStep={navigateToVisualEditor}\n onEditTransition={navigateToVisualEditor}\n onDeleteTransition={navigateToVisualEditor}\n onAddTransition={navigateToVisualEditor}\n />\n </div>\n )}\n\n {/* Event Triggers Section */}\n <div className=\"mt-8\">\n <DefinitionTriggersEditor\n value={triggers}\n onChange={setTriggers}\n />\n </div>\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AAoPU,SACE,KADF;AAlPV,YAAY,WAAW;AACvB,SAAS,WAAW,WAAW,mBAAmB;AAClD,SAAS,gBAAgB;AACzB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,cAAc;AACvB,SAAS,OAAO,wBAAwB;AACxC,SAAS,UAAU,4BAA4B;AAC/C,SAAS,iCAAiC;AAC1C,SAAS,6BAA6B,2CAA2C;AACjF,SAAS,cAAc,2BAA2B;AAClD,SAAS,oBAAoB;AAC7B,SAAS,qCAAqC;AAC9C,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,gCAAgC;AACzC,SAAS,8BAA8B;AACvC,SAAS,mBAAmB;AAGb,SAAR,6BAA8C;AACnD,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,UAAU;AACzB,QAAM,WAAW,YAAY;AAC7B,QAAM,IAAI,KAAK;AACf,QAAM,WAAW,YAAY;AAG7B,MAAI;AACJ,MAAI,QAAQ,QAAQ,MAAM,QAAQ,OAAO,IAAI,GAAG;AAC9C,mBAAe,OAAO,KAAK,CAAC;AAAA,EAC9B,WAAW,QAAQ,IAAI;AACrB,mBAAe,MAAM,QAAQ,OAAO,EAAE,IAAI,OAAO,GAAG,CAAC,IAAI,OAAO;AAAA,EAClE;AAEA,QAAM,EAAE,MAAM,YAAY,WAAW,MAAM,IAAI,SAAS;AAAA,IACtD,UAAU,CAAC,uBAAuB,YAAY;AAAA,IAC9C,SAAS,YAAY;AACnB,YAAM,WAAW,MAAM,SAAS,8BAA8B,YAAY,EAAE;AAC5E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,MAAM,IAAI,MAAM,EAAE,8BAA8B,CAAC;AACvD,YAAI,SAAS,SAAS;AACtB,cAAM;AAAA,MACR;AACA,YAAM,SAAS,MAAM,SAAS,KAAK;AACnC,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,SAAS,CAAC,CAAC;AAAA,IACX,OAAO,CAAC,cAAc,QAAQ;AAC5B,UAAK,IAA4B,WAAW,IAAK,QAAO;AACxD,aAAO,eAAe;AAAA,IACxB;AAAA,EACF,CAAC;AAED,QAAM,aAAc,OAAsC,WAAW;AAErE,QAAM,gBAAgB,MAAM,QAAQ,MAAM;AACxC,QAAI,YAAY;AACd,aAAO,0BAA0B,UAAU;AAAA,IAC7C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAsC,CAAC,CAAC;AAE9E,QAAM,UAAU,MAAM;AACpB,gBAAY,eAAe,YAAY,CAAC,CAAC;AAAA,EAC3C,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,SAAS,YAAY;AAC3B,QAAM,aAAa,WAAW;AAC9B,QAAM,iBAAiB,WAAW;AAElC,QAAM,EAAE,SAAS,qBAAqB,IAAI,iBAAiB;AAE3D,QAAM,oBAAoB,MAAM;AAAA,IAC9B,MAAM,gCAAgC,gBAAgB,SAAS;AAAA,IAC/D,CAAC,YAAY;AAAA,EACf;AACA,QAAM,EAAE,aAAa,kBAAkB,IAAI,mBAA4C;AAAA,IACrF,WAAW;AAAA,EACb,CAAC;AAED,QAAM,eAAe,OAAO,WAAyC;AACnE,UAAM,UAAU,qBAAqB,EAAE,GAAG,QAAQ,SAAS,CAAC;AAC5D,UAAM,uBAAuB;AAAA,MAC3B,OAAO,YAAY,cAAc,WAAW,WAAW,YAAY;AAAA,IACrE;AAEA,UAAM,YAAY;AAAA,MAChB,WAAW,YAAY;AACrB,cAAM,mBAAmB,MAAM,SAAS,8BAA8B,YAAY,IAAI;AAAA,UACpF,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC9B,CAAC;AACD,cAAM,WAAW,OAAO,KAAK,oBAAoB,EAAE,SAAS,IACxD,MAAM,qBAAqB,sBAAsB,gBAAgB,IACjE,MAAM,iBAAiB;AAC3B,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,YAAY,MAAM,aAMrB,UAAU,IAAI;AACjB,gBAAM,OAAO;AAAA,YACX,IAAI,MAAM,8BAA8B,WAAW,EAAE,+BAA+B,CAAC,CAAC;AAAA,YACtF,EAAE,QAAQ,SAAS,QAAQ,GAAI,aAAa,CAAC,EAAG;AAAA,UAClD;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,MACA,iBAAiB,EAAE,YAAY,cAAc,WAAW,SAAS;AAAA,MACjE,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,YAAY;AAAA,QACZ,WAAW;AAAA,QACX;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,KAAK,sBAAsB;AAClC,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,kBAAkB,YAAY;AAClC,QAAI;AACF,YAAM,SAAS,MAAM,YAAY;AAAA,QAC/B,WAAW,YAAY;AACrB,gBAAM,WAAW,MAAM,SAAS,8BAA8B,YAAY,cAAc;AAAA,YACtF,QAAQ;AAAA,UACV,CAAC;AACD,cAAI,CAAC,SAAS,IAAI;AAChB,kBAAM,YAAY,MAAM,aAAiC,UAAU,IAAI;AACvE,kBAAM,IAAI,MAAM,WAAW,SAAS,EAAE,+BAA+B,CAAC;AAAA,UACxE;AACA,iBAAO,aAAyC,UAAU,IAAI;AAAA,QAChE;AAAA,QACA,iBAAiB,EAAE,YAAY,cAAc,WAAW,YAAY;AAAA,QACpE,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,WAAW;AAAA,UACX;AAAA,QACF;AAAA,MACF,CAAC;AACD,UAAI,QAAQ,MAAM,IAAI;AACpB,eAAO,KAAK,wBAAwB,OAAO,KAAK,EAAE,EAAE;AACpD,eAAO,QAAQ;AAAA,MACjB;AAAA,IACF,SAASA,QAAO;AACd,YAAM,UAAUA,kBAAiB,QAAQA,OAAM,UAAU,EAAE,+BAA+B;AAC1F,YAAM,SAAS,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,oBAAoB,YAAY;AACpC,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,OAAO,EAAE,+BAA+B;AAAA,MACxC,aAAa,EAAE,gCAAgC;AAAA,MAC/C,aAAa,EAAE,+BAA+B;AAAA,MAC9C,SAAS;AAAA,IACX,CAAC;AACD,QAAI,CAAC,UAAW;AAEhB,QAAI;AACF,YAAM,SAAS,MAAM,YAAY;AAAA,QAC/B,WAAW,YAAY;AACrB,gBAAM,WAAW,MAAM,SAAS,8BAA8B,YAAY,kBAAkB;AAAA,YAC1F,QAAQ;AAAA,UACV,CAAC;AACD,cAAI,CAAC,SAAS,IAAI;AAChB,kBAAM,YAAY,MAAM,aAAiC,UAAU,IAAI;AACvE,kBAAM,IAAI,MAAM,WAAW,SAAS,EAAE,iCAAiC,CAAC;AAAA,UAC1E;AACA,iBAAO,aAAyC,UAAU,IAAI;AAAA,QAChE;AAAA,QACA,iBAAiB,EAAE,YAAY,cAAc,WAAW,gBAAgB;AAAA,QACxE,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,WAAW;AAAA,UACX;AAAA,QACF;AAAA,MACF,CAAC;AACD,YAAM,EAAE,4BAA4B,GAAG,SAAS;AAChD,YAAM,SAAS,QAAQ,MAAM,MAAM,QAAQ,YAAY,UAAU;AACjE,aAAO,KAAK,wBAAwB,MAAM,EAAE;AAC5C,aAAO,QAAQ;AAAA,IACjB,QAAQ;AACN,YAAM,EAAE,iCAAiC,GAAG,OAAO;AAAA,IACrD;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,QAAQ,MAAM,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC;AAEjE,QAAM,aAAa,MAAM;AAAA,IACvB,MAAM,WAAW,CAAC,IAAI,iBAAiB,GAAG,aAAa,iBAAiB;AAAA,IACxE,CAAC,GAAG,QAAQ;AAAA,EACd;AAEA,QAAM,yBAAyB,MAAM,YAAY,MAAM;AACrD,WAAO,KAAK,yCAAyC,YAAY,EAAE;AAAA,EACrE,GAAG,CAAC,QAAQ,YAAY,CAAC;AAOzB;AAAA,IACE,4BAA4B;AAAA,MAC1B,cAAc;AAAA,MACd,YAAY,gBAAgB;AAAA,MAC5B,WAAW,OAAO,YAAY,cAAc,WAAW,WAAW,YAAY;AAAA,MAC9E,MAAO,cAAc;AAAA,MACrB,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,MAAI,WAAW;AACb,WACE,oBAAC,QACC,8BAAC,YACC,+BAAC,SAAI,WAAU,kFACb;AAAA,0BAAC,WAAQ,WAAU,WAAU;AAAA,MAC7B,oBAAC,UAAM,YAAE,wBAAwB,GAAE;AAAA,OACrC,GACF,GACF;AAAA,EAEJ;AAEA,MAAI,YAAY;AACd,WACE,oBAAC,QACC,8BAAC,YACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,6BAA6B,EAAE,6BAA6B,CAAC;AAAA,QACtE,UAAS;AAAA,QACT,WAAW,EAAE,sBAAsB;AAAA;AAAA,IACrC,GACF,GACF;AAAA,EAEJ;AAEA,MAAI,SAAS,CAAC,YAAY;AACxB,WACE,oBAAC,QACC,+BAAC,YACC;AAAA,0BAAC,gBAAa,OAAO,EAAE,6BAA6B,GAAG;AAAA,MACvD,oBAAC,SAAI,WAAU,4BACb,8BAAC,UAAO,SAAO,MAAC,SAAQ,WACtB,8BAAC,OAAE,MAAK,wBAAwB,YAAE,sBAAsB,GAAE,GAC5D,GACF;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,MAAI,CAAC,eAAe;AAClB,WAAO;AAAA,EACT;AAEA,SACE,qBAAC,QACC;AAAA,yBAAC,YACE;AAAA,oBACC,oBAAC,SAAM,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useParams, usePathname } from 'next/navigation'\nimport { useQuery } from '@tanstack/react-query'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport { apiFetch, withScopedApiHeaders } from '@open-mercato/ui/backend/utils/api'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { buildRecordInjectionContext, useSetCurrentRecordInjectionContext } from '@open-mercato/ui/backend/injection/recordContext'\nimport { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { formatWorkflowValidationError } from '../../../lib/format-validation-error'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n parseWorkflowToFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { DefinitionTriggersEditor } from '../../../components/DefinitionTriggersEditor'\nimport { MobileDefinitionDetail } from '../../../components/mobile/MobileDefinitionDetail'\nimport { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'\nimport type { WorkflowDefinitionTrigger } from '../../../data/entities'\n\nexport default function EditWorkflowDefinitionPage() {\n const router = useRouter()\n const params = useParams()\n const pathname = usePathname()\n const t = useT()\n const isMobile = useIsMobile()\n\n // Handle catch-all route: params.slug = ['definitions', 'uuid']\n let definitionId: string | undefined\n if (params?.slug && Array.isArray(params.slug)) {\n definitionId = params.slug[1] // Second element is the ID\n } else if (params?.id) {\n definitionId = Array.isArray(params.id) ? params.id[0] : params.id\n }\n\n const { data: definition, isLoading, error } = useQuery({\n queryKey: ['workflow-definition', definitionId],\n queryFn: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}`)\n if (!response.ok) {\n const err = new Error(t('workflows.errors.fetchFailed')) as Error & { status?: number }\n err.status = response.status\n throw err\n }\n const result = await response.json()\n return result.data\n },\n enabled: !!definitionId,\n retry: (failureCount, err) => {\n if ((err as { status?: number }).status === 404) return false\n return failureCount < 2\n },\n })\n\n const isNotFound = (error as { status?: number } | null)?.status === 404\n\n const initialValues = React.useMemo(() => {\n if (definition) {\n return parseWorkflowToFormValues(definition)\n }\n return null\n }, [definition])\n\n const [triggers, setTriggers] = React.useState<WorkflowDefinitionTrigger[]>([])\n\n React.useEffect(() => {\n setTriggers(initialValues?.triggers ?? [])\n }, [initialValues])\n\n const source = definition?.source as 'code' | 'code_override' | 'user' | undefined\n const isCodeOnly = source === 'code'\n const isCodeOverride = source === 'code_override'\n\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const mutationContextId = React.useMemo(\n () => `workflows.definitions.detail:${definitionId ?? 'unknown'}`,\n [definitionId],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: mutationContextId,\n })\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload({ ...values, triggers })\n const optimisticLockHeader = buildOptimisticLockHeader(\n typeof definition?.updatedAt === 'string' ? definition.updatedAt : null,\n )\n\n await runMutation({\n operation: async () => {\n const updateDefinition = () => apiFetch(`/api/workflows/definitions/${definitionId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n const response = Object.keys(optimisticLockHeader).length > 0\n ? await withScopedApiHeaders(optimisticLockHeader, updateDefinition)\n : await updateDefinition()\n if (!response.ok) {\n const errorBody = await readJsonSafe<{\n error?: string\n code?: string\n currentUpdatedAt?: string\n expectedUpdatedAt?: string\n details?: Array<{ path?: Array<string | number>; message?: string }>\n }>(response, null)\n throw Object.assign(\n new Error(formatWorkflowValidationError(errorBody, t('workflows.errors.updateFailed'))),\n { status: response.status, ...(errorBody ?? {}) },\n )\n }\n return response\n },\n mutationPayload: { resourceId: definitionId, operation: 'update' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'update',\n retryLastMutation,\n },\n })\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const handleCustomize = async () => {\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/customize`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.errors.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'customize' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'customize',\n retryLastMutation,\n },\n })\n if (result?.data?.id) {\n router.push(`/backend/definitions/${result.data.id}`)\n router.refresh()\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('workflows.errors.updateFailed')\n flash(message, 'error')\n }\n }\n\n const handleResetToCode = async () => {\n const confirmed = await confirm({\n title: t('workflows.actions.resetToCode'),\n description: t('workflows.actions.resetConfirm'),\n confirmText: t('workflows.actions.resetToCode'),\n variant: 'destructive',\n })\n if (!confirmed) return\n\n try {\n const result = await runMutation({\n operation: async () => {\n const response = await apiFetch(`/api/workflows/definitions/${definitionId}/reset-to-code`, {\n method: 'POST',\n })\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string }>(response, null)\n throw new Error(errorBody?.error || t('workflows.messages.updateFailed'))\n }\n return readJsonSafe<{ data?: { id?: string } }>(response, null)\n },\n mutationPayload: { resourceId: definitionId, operation: 'reset-to-code' },\n context: {\n formId: mutationContextId,\n resourceKind: 'workflows.definition',\n resourceId: definitionId,\n operation: 'reset-to-code',\n retryLastMutation,\n },\n })\n flash(t('workflows.messages.updated'), 'success')\n const codeId = result?.data?.id || `code:${definition?.workflowId}`\n router.push(`/backend/definitions/${codeId}`)\n router.refresh()\n } catch {\n flash(t('workflows.messages.updateFailed'), 'error')\n }\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => isMobile ? [] : createFormGroups(t, StepsEditor, TransitionsEditor),\n [t, isMobile]\n )\n\n const navigateToVisualEditor = React.useCallback(() => {\n router.push(`/backend/definitions/visual-editor?id=${definitionId}`)\n }, [router, definitionId])\n\n // Publish page-load record context to the AppShell-owned `backend:record:current`\n // mount so the enterprise record_locks widget resolves `workflows.definition` + id\n // explicitly. The resourceKind mirrors the save-time `useGuardedMutation` context\n // and server guard so the held lock matches the conflict surface for the same\n // definition across both the form and visual editors.\n useSetCurrentRecordInjectionContext(\n buildRecordInjectionContext({\n resourceKind: 'workflows.definition',\n resourceId: definitionId ?? null,\n updatedAt: typeof definition?.updatedAt === 'string' ? definition.updatedAt : null,\n data: (definition ?? null) as Record<string, unknown> | null,\n path: pathname,\n }),\n )\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <div className=\"flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground\">\n <Spinner className=\"h-6 w-6\" />\n <span>{t('workflows.edit.loading')}</span>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('workflows.errors.notFound', t('workflows.errors.loadFailed'))}\n backHref=\"/backend/definitions\"\n backLabel={t('workflows.backToList')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !definition) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={t('workflows.errors.loadFailed')} />\n <div className=\"mt-4 flex justify-center\">\n <Button asChild variant=\"outline\">\n <a href=\"/backend/definitions\">{t('workflows.backToList')}</a>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n if (!initialValues) {\n return null\n }\n\n return (\n <Page>\n <PageBody>\n {isCodeOnly && (\n <Alert status=\"information\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code.readonlyBanner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleCustomize}>\n {t('workflows.actions.customize')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n {isCodeOverride && (\n <Alert status=\"warning\" className=\"mb-4\">\n <AlertDescription className=\"flex items-center justify-between\">\n <span>{t('workflows.source.code_override.banner')}</span>\n <Button variant=\"outline\" size=\"sm\" onClick={handleResetToCode}>\n {t('workflows.actions.resetToCode')}\n </Button>\n </AlertDescription>\n </Alert>\n )}\n <div className=\"mb-4 p-4 bg-status-info-bg border border-status-info-border rounded-lg flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-start gap-3\">\n <svg className=\"w-5 h-5 text-status-info-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-info-text\">\n {t('workflows.edit.visualEditorAvailable')}\n </p>\n <p className=\"text-xs text-status-info-text mt-0.5\">\n {t('workflows.edit.visualEditorDescription')}\n </p>\n </div>\n </div>\n <Button asChild variant=\"outline\" size=\"sm\" className=\"w-full sm:w-auto border-status-info-border text-status-info-text hover:bg-status-info-bg\">\n <a href={`/backend/definitions/visual-editor?id=${definitionId}`}>\n {t('workflows.actions.openVisualEditor')}\n </a>\n </Button>\n </div>\n <CrudForm\n key={definitionId}\n title={isCodeOnly ? definition?.workflowName || t('workflows.edit.title') : t('workflows.edit.title')}\n backHref=\"/backend/definitions\"\n schema={workflowDefinitionFormSchema}\n fields={fields}\n initialValues={initialValues}\n onSubmit={handleSubmit}\n cancelHref=\"/backend/definitions\"\n groups={formGroups}\n submitLabel={isCodeOnly ? t('workflows.actions.customize') : t('workflows.form.update')}\n {...(isCodeOnly ? { readOnly: true } : {})}\n />\n\n {/* Mobile Steps & Transitions View */}\n {isMobile && initialValues && (\n <div className=\"mt-4\">\n <MobileDefinitionDetail\n values={initialValues}\n onEditStep={navigateToVisualEditor}\n onDeleteStep={navigateToVisualEditor}\n onAddStep={navigateToVisualEditor}\n onEditTransition={navigateToVisualEditor}\n onDeleteTransition={navigateToVisualEditor}\n onAddTransition={navigateToVisualEditor}\n />\n </div>\n )}\n\n {/* Event Triggers Section */}\n <div className=\"mt-8\">\n <DefinitionTriggersEditor\n value={triggers}\n onChange={setTriggers}\n />\n </div>\n </PageBody>\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["error"]
|
|
7
7
|
}
|
|
@@ -42,7 +42,7 @@ function CreateWorkflowDefinitionPage() {
|
|
|
42
42
|
[t]
|
|
43
43
|
);
|
|
44
44
|
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsxs(PageBody, { children: [
|
|
45
|
-
/* @__PURE__ */ jsxs(Alert, {
|
|
45
|
+
/* @__PURE__ */ jsxs(Alert, { status: "information", icon: /* @__PURE__ */ jsx(Zap, { "aria-hidden": "true" }), className: "mb-6", children: [
|
|
46
46
|
/* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.create.eventTriggersTitle") }),
|
|
47
47
|
/* @__PURE__ */ jsx(AlertDescription, { children: t("workflows.create.eventTriggersDescription") })
|
|
48
48
|
] }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/workflows/backend/definitions/create/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n defaultFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Zap } from 'lucide-react'\nimport { formatWorkflowValidationError } from '../../../lib/format-validation-error'\n\nexport default function CreateWorkflowDefinitionPage() {\n const router = useRouter()\n const t = useT()\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload(values)\n\n const response = await apiFetch('/api/workflows/definitions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string; details?: Array<{ path?: Array<string | number>; message?: string }> }>(response, null)\n throw new Error(formatWorkflowValidationError(errorBody, t('workflows.errors.createFailed')))\n }\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => createFormGroups(t, StepsEditor, TransitionsEditor),\n [t]\n )\n\n return (\n <Page>\n <PageBody>\n <Alert
|
|
5
|
-
"mappings": ";AAuDQ,
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm } from '@open-mercato/ui/backend/CrudForm'\nimport { apiFetch } from '@open-mercato/ui/backend/utils/api'\nimport { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n workflowDefinitionFormSchema,\n createFormGroups,\n createFieldDefinitions,\n defaultFormValues,\n buildWorkflowPayload,\n type WorkflowDefinitionFormValues,\n} from '../../../components/formConfig'\nimport { StepsEditor } from '../../../components/StepsEditor'\nimport { TransitionsEditor } from '../../../components/TransitionsEditor'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Zap } from 'lucide-react'\nimport { formatWorkflowValidationError } from '../../../lib/format-validation-error'\n\nexport default function CreateWorkflowDefinitionPage() {\n const router = useRouter()\n const t = useT()\n\n const handleSubmit = async (values: WorkflowDefinitionFormValues) => {\n const payload = buildWorkflowPayload(values)\n\n const response = await apiFetch('/api/workflows/definitions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) {\n const errorBody = await readJsonSafe<{ error?: string; details?: Array<{ path?: Array<string | number>; message?: string }> }>(response, null)\n throw new Error(formatWorkflowValidationError(errorBody, t('workflows.errors.createFailed')))\n }\n\n router.push('/backend/definitions')\n router.refresh()\n }\n\n const fields = React.useMemo(() => createFieldDefinitions(t), [t])\n\n const formGroups = React.useMemo(\n () => createFormGroups(t, StepsEditor, TransitionsEditor),\n [t]\n )\n\n return (\n <Page>\n <PageBody>\n <Alert status=\"information\" icon={<Zap aria-hidden=\"true\" />} className=\"mb-6\">\n <AlertTitle>{t('workflows.create.eventTriggersTitle')}</AlertTitle>\n <AlertDescription>\n {t('workflows.create.eventTriggersDescription')}\n </AlertDescription>\n </Alert>\n <CrudForm\n title={t('workflows.create.title')}\n backHref=\"/backend/definitions\"\n schema={workflowDefinitionFormSchema}\n fields={fields}\n initialValues={defaultFormValues}\n onSubmit={handleSubmit}\n cancelHref=\"/backend/definitions\"\n groups={formGroups}\n submitLabel={t('workflows.form.create')}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAuDQ,SAAkC,KAAlC;AArDR,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,MAAM,gBAAgB;AAC/B,SAAS,gBAAgB;AACzB,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAC7B,SAAS,YAAY;AACrB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,OAAO,kBAAkB,kBAAkB;AACpD,SAAS,WAAW;AACpB,SAAS,qCAAqC;AAE/B,SAAR,+BAAgD;AACrD,QAAM,SAAS,UAAU;AACzB,QAAM,IAAI,KAAK;AAEf,QAAM,eAAe,OAAO,WAAyC;AACnE,UAAM,UAAU,qBAAqB,MAAM;AAE3C,UAAM,WAAW,MAAM,SAAS,8BAA8B;AAAA,MAC5D,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,IAC9B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,aAAuG,UAAU,IAAI;AAC7I,YAAM,IAAI,MAAM,8BAA8B,WAAW,EAAE,+BAA+B,CAAC,CAAC;AAAA,IAC9F;AAEA,WAAO,KAAK,sBAAsB;AAClC,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,SAAS,MAAM,QAAQ,MAAM,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC;AAEjE,QAAM,aAAa,MAAM;AAAA,IACvB,MAAM,iBAAiB,GAAG,aAAa,iBAAiB;AAAA,IACxD,CAAC,CAAC;AAAA,EACJ;AAEA,SACE,oBAAC,QACC,+BAAC,YACC;AAAA,yBAAC,SAAM,QAAO,eAAc,MAAM,oBAAC,OAAI,eAAY,QAAO,GAAI,WAAU,QACtE;AAAA,0BAAC,cAAY,YAAE,qCAAqC,GAAE;AAAA,MACtD,oBAAC,oBACE,YAAE,2CAA2C,GAChD;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,wBAAwB;AAAA,QACjC,UAAS;AAAA,QACT,QAAQ;AAAA,QACR;AAAA,QACA,eAAe;AAAA,QACf,UAAU;AAAA,QACV,YAAW;AAAA,QACX,QAAQ;AAAA,QACR,aAAa,EAAE,uBAAuB;AAAA;AAAA,IACxC;AAAA,KACF,GACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -690,8 +690,8 @@ function VisualEditorPage() {
|
|
|
690
690
|
}
|
|
691
691
|
) }),
|
|
692
692
|
(isCodeOnly || isCodeOverride) && /* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: [
|
|
693
|
-
isCodeOnly && /* @__PURE__ */ jsx(Alert, {
|
|
694
|
-
isCodeOverride && /* @__PURE__ */ jsx(Alert, {
|
|
693
|
+
isCodeOnly && /* @__PURE__ */ jsx(Alert, { status: "information", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code.readonlyBanner") }) }),
|
|
694
|
+
isCodeOverride && /* @__PURE__ */ jsx(Alert, { status: "warning", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code_override.banner") }) })
|
|
695
695
|
] }),
|
|
696
696
|
showMetadata && /* @__PURE__ */ jsxs("div", { className: isCompactViewport ? "shrink-0 border-b border-border bg-background px-3 py-2 max-h-[60svh] overflow-y-auto overscroll-contain md:px-6 md:py-3" : "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: [
|
|
697
697
|
/* @__PURE__ */ jsxs("fieldset", { disabled: isCodeOnly, className: "rounded-lg border bg-card p-3 disabled:opacity-70 md:p-4", children: [
|
|
@@ -1009,7 +1009,7 @@ function VisualEditorPage() {
|
|
|
1009
1009
|
}
|
|
1010
1010
|
)
|
|
1011
1011
|
] }),
|
|
1012
|
-
/* @__PURE__ */ jsxs(Alert, {
|
|
1012
|
+
/* @__PURE__ */ jsxs(Alert, { status: "information", className: "mt-6", children: [
|
|
1013
1013
|
/* @__PURE__ */ jsx(AlertTitle, { className: "text-xs", children: t("workflows.visualEditor.howToUse", "How to use:") }),
|
|
1014
1014
|
/* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs("ul", { className: "list-inside list-disc space-y-1 text-xs", children: [
|
|
1015
1015
|
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.addSteps", "Click step types to add them") }),
|