@open-mercato/core 0.6.7-develop.6834.1.e76f4b8cbc → 0.6.7-develop.6851.1.c1680b00d8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +2 -3
  3. package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +2 -2
  4. package/dist/modules/dashboards/lib/aggregations.js +20 -6
  5. package/dist/modules/dashboards/lib/aggregations.js.map +2 -2
  6. package/dist/modules/entities/api/definitions.js +2 -0
  7. package/dist/modules/entities/api/definitions.js.map +2 -2
  8. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +18 -3
  9. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  10. package/dist/modules/entities/components/RecordListRelationCell.js +63 -0
  11. package/dist/modules/entities/components/RecordListRelationCell.js.map +7 -0
  12. package/dist/modules/entities/components/recordListRelationDisplays.js +80 -0
  13. package/dist/modules/entities/components/recordListRelationDisplays.js.map +7 -0
  14. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +14 -1
  15. package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
  16. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +22 -17
  17. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  18. package/dist/modules/query_index/api/reindex.js +10 -0
  19. package/dist/modules/query_index/api/reindex.js.map +2 -2
  20. package/dist/modules/query_index/cli.js +125 -1
  21. package/dist/modules/query_index/cli.js.map +2 -2
  22. package/dist/modules/query_index/lib/coverage.js +19 -7
  23. package/dist/modules/query_index/lib/coverage.js.map +2 -2
  24. package/dist/modules/query_index/lib/reindexer.js +43 -37
  25. package/dist/modules/query_index/lib/reindexer.js.map +2 -2
  26. package/dist/modules/sales/commands/documents.js +27 -0
  27. package/dist/modules/sales/commands/documents.js.map +2 -2
  28. package/dist/modules/sales/data/validators.js +2 -0
  29. package/dist/modules/sales/data/validators.js.map +2 -2
  30. package/package.json +8 -8
  31. package/src/modules/api_keys/i18n/ko.json +42 -0
  32. package/src/modules/attachments/i18n/ko.json +172 -0
  33. package/src/modules/audit_logs/i18n/ko.json +66 -0
  34. package/src/modules/auth/i18n/ko.json +255 -0
  35. package/src/modules/business_rules/i18n/ko.json +396 -0
  36. package/src/modules/catalog/i18n/ko.json +723 -0
  37. package/src/modules/communication_channels/i18n/ko.json +138 -0
  38. package/src/modules/configs/i18n/ko.json +197 -0
  39. package/src/modules/currencies/i18n/de.json +1 -0
  40. package/src/modules/currencies/i18n/en.json +1 -0
  41. package/src/modules/currencies/i18n/es.json +1 -0
  42. package/src/modules/currencies/i18n/ko.json +160 -0
  43. package/src/modules/currencies/i18n/pl.json +1 -0
  44. package/src/modules/customer_accounts/i18n/ko.json +354 -0
  45. package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +2 -3
  46. package/src/modules/customers/i18n/de.json +10 -0
  47. package/src/modules/customers/i18n/en.json +10 -0
  48. package/src/modules/customers/i18n/es.json +10 -0
  49. package/src/modules/customers/i18n/ko.json +2855 -0
  50. package/src/modules/customers/i18n/pl.json +10 -0
  51. package/src/modules/dashboards/i18n/ko.json +118 -0
  52. package/src/modules/dashboards/lib/aggregations.ts +42 -6
  53. package/src/modules/data_sync/i18n/ko.json +125 -0
  54. package/src/modules/design_system/i18n/ko.json +58 -0
  55. package/src/modules/dictionaries/i18n/ko.json +130 -0
  56. package/src/modules/directory/i18n/ko.json +105 -0
  57. package/src/modules/entities/api/definitions.ts +4 -0
  58. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +94 -77
  59. package/src/modules/entities/components/RecordListRelationCell.tsx +90 -0
  60. package/src/modules/entities/components/recordListRelationDisplays.ts +109 -0
  61. package/src/modules/entities/i18n/ko.json +260 -0
  62. package/src/modules/feature_toggles/i18n/ko.json +96 -0
  63. package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +17 -1
  64. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +21 -16
  65. package/src/modules/inbox_ops/components/proposals/types.ts +1 -0
  66. package/src/modules/inbox_ops/i18n/ko.json +196 -0
  67. package/src/modules/integrations/i18n/ko.json +129 -0
  68. package/src/modules/messages/i18n/ko.json +263 -0
  69. package/src/modules/notifications/i18n/ko.json +55 -0
  70. package/src/modules/payment_gateways/i18n/ko.json +87 -0
  71. package/src/modules/planner/i18n/ko.json +565 -0
  72. package/src/modules/portal/i18n/ko.json +143 -0
  73. package/src/modules/progress/i18n/ko.json +20 -0
  74. package/src/modules/query_index/api/reindex.ts +15 -0
  75. package/src/modules/query_index/cli.ts +180 -1
  76. package/src/modules/query_index/i18n/ko.json +69 -0
  77. package/src/modules/query_index/lib/coverage.ts +38 -8
  78. package/src/modules/query_index/lib/reindexer.ts +62 -40
  79. package/src/modules/resources/i18n/ko.json +770 -0
  80. package/src/modules/sales/commands/documents.ts +49 -0
  81. package/src/modules/sales/data/validators.ts +2 -0
  82. package/src/modules/sales/i18n/ko.json +1442 -0
  83. package/src/modules/shipping_carriers/i18n/ko.json +85 -0
  84. package/src/modules/staff/i18n/ko.json +1162 -0
  85. package/src/modules/sync_excel/i18n/ko.json +107 -0
  86. package/src/modules/translations/i18n/ko.json +45 -0
  87. package/src/modules/wms/i18n/ko.json +1053 -0
  88. package/src/modules/workflows/i18n/ko.json +1203 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/inbox_ops/components/proposals/EditActionDialog.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Loader2 } from 'lucide-react'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n DialogDescription,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport type { ActionDetail } from './types'\nimport { useActionDescriptionResolver } from './ActionCard'\n\nfunction ShipmentPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n const trackingNumbers = ((payload.trackingNumbers as string[]) || []).join(', ')\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.carrier', 'Carrier')}</Label>\n <Input value={(payload.carrierName as string) || ''} onChange={(event) => updateField('carrierName', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.status', 'Status')}</Label>\n <Input value={(payload.statusLabel as string) || ''} onChange={(event) => updateField('statusLabel', event.target.value)} />\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.tracking_numbers', 'Tracking Numbers')}</Label>\n <Input\n value={trackingNumbers}\n onChange={(event) => updateField('trackingNumbers', event.target.value.split(',').map((s) => s.trim()).filter(Boolean))}\n placeholder={t('inbox_ops.placeholder.comma_separated', 'Comma-separated')}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.shipped_at', 'Shipped At')}</Label>\n <Input value={(payload.shippedAt as string) || ''} onChange={(event) => updateField('shippedAt', event.target.value)} placeholder=\"YYYY-MM-DD\" />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.estimated_delivery', 'Estimated Delivery')}</Label>\n <Input value={(payload.estimatedDelivery as string) || ''} onChange={(event) => updateField('estimatedDelivery', event.target.value)} placeholder=\"YYYY-MM-DD\" />\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.notes', 'Notes')}</Label>\n <Textarea value={(payload.notes as string) || ''} onChange={(event) => updateField('notes', event.target.value)} rows={2} />\n </div>\n </div>\n )\n}\n\nfunction ContactPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n const type = (payload.type as string) || 'person'\n const isPerson = type === 'person'\n\n const existingName = (payload.name as string) || ''\n const nameParts = existingName.trim().split(/\\s+/).filter((p) => p.length > 0)\n const [firstName, setFirstName] = React.useState(() => isPerson ? (nameParts[0] || '') : '')\n const [lastName, setLastName] = React.useState(() => isPerson ? (nameParts.slice(1).join(' ') || '') : '')\n\n const updatePersonName = React.useCallback((first: string, last: string) => {\n const combined = `${first} ${last}`.trim()\n updateField('name', combined)\n }, [updateField])\n\n const lastNameMissing = isPerson && !lastName.trim()\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n {isPerson ? (\n <>\n <div>\n <Label>{t('inbox_ops.contact.first_name', 'First Name')}</Label>\n <Input\n value={firstName}\n onChange={(event) => {\n setFirstName(event.target.value)\n updatePersonName(event.target.value, lastName)\n }}\n />\n </div>\n <div>\n <Label>{t('inbox_ops.contact.last_name', 'Last Name')}</Label>\n <Input\n value={lastName}\n onChange={(event) => {\n setLastName(event.target.value)\n updatePersonName(firstName, event.target.value)\n }}\n className={lastNameMissing ? 'border-status-error-border' : ''}\n />\n {lastNameMissing && (\n <p className=\"text-xs text-status-error-text mt-1\">{t('inbox_ops.contact.last_name_required', 'Last name is required')}</p>\n )}\n </div>\n </>\n ) : (\n <div>\n <Label>{t('inbox_ops.edit_dialog.name', 'Name')}</Label>\n <Input value={existingName} onChange={(event) => updateField('name', event.target.value)} />\n </div>\n )}\n <div>\n <Label>{t('inbox_ops.edit_dialog.type', 'Type')}</Label>\n <Select\n value={type}\n onValueChange={(value) => updateField('type', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"person\">{t('inbox_ops.contact_type.person', 'Person')}</SelectItem>\n <SelectItem value=\"company\">{t('inbox_ops.contact_type.company', 'Company')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.email', 'Email')}</Label>\n <Input value={(payload.email as string) || ''} onChange={(event) => updateField('email', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.phone', 'Phone')}</Label>\n <Input value={(payload.phone as string) || ''} onChange={(event) => updateField('phone', event.target.value)} />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.company_name', 'Company Name')}</Label>\n <Input value={(payload.companyName as string) || ''} onChange={(event) => updateField('companyName', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.role', 'Role')}</Label>\n <Input value={(payload.role as string) || ''} onChange={(event) => updateField('role', event.target.value)} />\n </div>\n </div>\n </div>\n )\n}\n\nfunction LinkContactPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.email', 'Email')}</Label>\n <Input value={(payload.emailAddress as string) || ''} onChange={(event) => updateField('emailAddress', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_id', 'Contact ID')}</Label>\n <Input value={(payload.contactId as string) || ''} onChange={(event) => updateField('contactId', event.target.value)} />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_type', 'Contact Type')}</Label>\n <Select\n value={(payload.contactType as string) || 'person'}\n onValueChange={(value) => updateField('contactType', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"person\">{t('inbox_ops.contact_type.person', 'Person')}</SelectItem>\n <SelectItem value=\"company\">{t('inbox_ops.contact_type.company', 'Company')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_name', 'Contact Name')}</Label>\n <Input value={(payload.contactName as string) || ''} onChange={(event) => updateField('contactName', event.target.value)} />\n </div>\n </div>\n </div>\n )\n}\n\nfunction DraftReplyPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.to', 'To')}</Label>\n <Input value={(payload.to as string) || ''} onChange={(event) => updateField('to', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.to_name', 'To Name')}</Label>\n <Input value={(payload.toName as string) || ''} onChange={(event) => updateField('toName', event.target.value)} />\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.subject', 'Subject')}</Label>\n <Input value={(payload.subject as string) || ''} onChange={(event) => updateField('subject', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.body', 'Body')}</Label>\n <Textarea value={(payload.body as string) || ''} onChange={(event) => updateField('body', event.target.value)} rows={6} />\n </div>\n </div>\n )\n}\n\nfunction UpdateOrderPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n const quantityChanges = (payload.quantityChanges as Record<string, unknown>[]) || []\n const deliveryDateChange = (payload.deliveryDateChange as Record<string, unknown>) || {}\n const noteAdditions = Array.isArray(payload.noteAdditions) ? (payload.noteAdditions as string[]).join('\\n') : ''\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.order_id', 'Order ID')}</Label>\n <Input value={(payload.orderId as string) || ''} onChange={(event) => updateField('orderId', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.order_number', 'Order Number')}</Label>\n <Input value={(payload.orderNumber as string) || ''} onChange={(event) => updateField('orderNumber', event.target.value)} />\n </div>\n </div>\n\n {quantityChanges.length > 0 && (\n <div>\n <Label className=\"mb-1 block\">{t('inbox_ops.edit_dialog.quantity_changes', 'Quantity Changes')}</Label>\n <div className=\"space-y-2\">\n {quantityChanges.map((change, index) => (\n <div key={index} className=\"border rounded p-2 space-y-2\">\n <div className=\"grid grid-cols-3 gap-2\">\n <div>\n <Label className=\"text-xs\">{t('inbox_ops.edit_dialog.line_item', 'Line Item')}</Label>\n <Input\n className=\"h-8 text-sm\"\n value={(change.lineItemName as string) || ''}\n onChange={(event) => {\n const arr = [...quantityChanges]\n arr[index] = { ...arr[index], lineItemName: event.target.value }\n updateField('quantityChanges', arr)\n }}\n />\n </div>\n <div>\n <Label className=\"text-xs\">{t('inbox_ops.edit_dialog.old_qty', 'Old Qty')}</Label>\n <Input\n className=\"h-8 text-sm\"\n value={(change.oldQuantity as string) || ''}\n onChange={(event) => {\n const arr = [...quantityChanges]\n arr[index] = { ...arr[index], oldQuantity: event.target.value }\n updateField('quantityChanges', arr)\n }}\n />\n </div>\n <div>\n <Label className=\"text-xs\">{t('inbox_ops.edit_dialog.new_qty', 'New Qty')}</Label>\n <Input\n className=\"h-8 text-sm\"\n value={(change.newQuantity as string) || ''}\n onChange={(event) => {\n const arr = [...quantityChanges]\n arr[index] = { ...arr[index], newQuantity: event.target.value }\n updateField('quantityChanges', arr)\n }}\n />\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {Boolean(deliveryDateChange.oldDate || deliveryDateChange.newDate) && (\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.old_delivery_date', 'Old Delivery Date')}</Label>\n <Input\n value={(deliveryDateChange.oldDate as string) || ''}\n onChange={(event) => updateField('deliveryDateChange', { ...deliveryDateChange, oldDate: event.target.value })}\n placeholder=\"YYYY-MM-DD\"\n />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.new_delivery_date', 'New Delivery Date')}</Label>\n <Input\n value={(deliveryDateChange.newDate as string) || ''}\n onChange={(event) => updateField('deliveryDateChange', { ...deliveryDateChange, newDate: event.target.value })}\n placeholder=\"YYYY-MM-DD\"\n />\n </div>\n </div>\n )}\n\n <div>\n <Label>{t('inbox_ops.edit_dialog.note_additions', 'Notes')}</Label>\n <Textarea\n value={noteAdditions}\n onChange={(event) => {\n const lines = event.target.value.split('\\n').filter(Boolean)\n updateField('noteAdditions', lines.length > 0 ? lines : undefined)\n }}\n rows={2}\n placeholder={t('inbox_ops.placeholder.one_note_per_line', 'One note per line')}\n />\n </div>\n </div>\n )\n}\n\nfunction LogActivityPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_name', 'Contact Name')}</Label>\n <Input value={(payload.contactName as string) || ''} onChange={(event) => updateField('contactName', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_type', 'Contact Type')}</Label>\n <Select\n value={(payload.contactType as string) || 'person'}\n onValueChange={(value) => updateField('contactType', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"person\">{t('inbox_ops.contact_type.person', 'Person')}</SelectItem>\n <SelectItem value=\"company\">{t('inbox_ops.contact_type.company', 'Company')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.activity_type', 'Activity Type')}</Label>\n <Select\n value={(payload.activityType as string) || 'email'}\n onValueChange={(value) => updateField('activityType', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"email\">{t('inbox_ops.activity_type.email', 'Email')}</SelectItem>\n <SelectItem value=\"call\">{t('inbox_ops.activity_type.call', 'Call')}</SelectItem>\n <SelectItem value=\"meeting\">{t('inbox_ops.activity_type.meeting', 'Meeting')}</SelectItem>\n <SelectItem value=\"note\">{t('inbox_ops.activity_type.note', 'Note')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.subject', 'Subject')}</Label>\n <Input value={(payload.subject as string) || ''} onChange={(event) => updateField('subject', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.body', 'Body')}</Label>\n <Textarea value={(payload.body as string) || ''} onChange={(event) => updateField('body', event.target.value)} rows={6} />\n </div>\n </div>\n )\n}\n\nexport function EditActionDialog({\n action,\n actionTypeLabels,\n onClose,\n onSaved,\n}: {\n action: ActionDetail\n actionTypeLabels: Record<string, string>\n onClose: () => void\n onSaved: () => void\n}) {\n const t = useT()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'inbox-ops-edit-action',\n })\n const [payload, setPayload] = React.useState<Record<string, unknown>>(\n () => structuredClone(action.payload),\n )\n const [isSaving, setIsSaving] = React.useState(false)\n const [jsonMode, setJsonMode] = React.useState(false)\n const [jsonText, setJsonText] = React.useState(() => JSON.stringify(action.payload, null, 2))\n const [jsonError, setJsonError] = React.useState<string | null>(null)\n\n const handleSave = React.useCallback(async () => {\n let finalPayload = payload\n if (jsonMode) {\n try {\n finalPayload = JSON.parse(jsonText)\n setJsonError(null)\n } catch {\n setJsonError(t('inbox_ops.edit_dialog.invalid_json', 'Invalid JSON'))\n return\n }\n }\n\n setIsSaving(true)\n const result = await runMutation({\n // TODO(#2373-D): thread updatedAt \u2014 ActionDetail type lives outside this file's edit scope\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(undefined),\n () => apiCall<{ ok: boolean; error?: string }>(\n `/api/inbox_ops/proposals/${action.proposalId}/actions/${action.id}`,\n { method: 'PATCH', body: JSON.stringify({ payload: finalPayload }) },\n ),\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.edit_dialog.saved', 'Action updated successfully'), 'success')\n onSaved()\n onClose()\n } else {\n flash(result?.result?.error || t('inbox_ops.flash.save_failed', 'Failed to save'), 'error')\n }\n setIsSaving(false)\n }, [action, payload, jsonMode, jsonText, t, onSaved, onClose, runMutation])\n\n React.useEffect(() => {\n function handleKeyDown(event: KeyboardEvent) {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleSave()\n }\n }\n window.addEventListener('keydown', handleKeyDown)\n return () => window.removeEventListener('keydown', handleKeyDown)\n }, [handleSave])\n\n const updateField = (key: string, value: unknown) => {\n setPayload((prev) => ({ ...prev, [key]: value }))\n }\n\n const resolveActionDescription = useActionDescriptionResolver()\n const label = actionTypeLabels[action.actionType] || action.actionType\n const hasTypedEditor = [\n 'create_order', 'create_quote', 'update_order', 'update_shipment', 'create_contact', 'link_contact', 'log_activity', 'draft_reply',\n ].includes(action.actionType)\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose() }}>\n <DialogContent className=\"sm:max-w-2xl sm:max-h-[85vh]\">\n <DialogHeader>\n <DialogTitle>{t('inbox_ops.edit_dialog.title', 'Edit Action')}: {label}</DialogTitle>\n <DialogDescription>{resolveActionDescription(action.description, action.payload)}</DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 overflow-y-auto max-h-[60vh] py-2\">\n {hasTypedEditor && !jsonMode && (\n <>\n {action.actionType === 'update_order' && (\n <UpdateOrderPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'update_shipment' && (\n <ShipmentPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'create_contact' && (\n <ContactPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'link_contact' && (\n <LinkContactPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'log_activity' && (\n <LogActivityPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'draft_reply' && (\n <DraftReplyPayloadEditor payload={payload} updateField={updateField} />\n )}\n </>\n )}\n\n {(!hasTypedEditor || jsonMode) && (\n <div className=\"space-y-2\">\n <Label>{t('inbox_ops.edit_dialog.payload_json', 'Payload (JSON)')}</Label>\n <Textarea\n className=\"font-mono text-xs min-h-[200px]\"\n value={jsonText}\n onChange={(event) => {\n setJsonText(event.target.value)\n setJsonError(null)\n }}\n />\n {jsonError && <p className=\"text-xs text-status-error-text\">{t('inbox_ops.edit_dialog.invalid_json', 'Invalid JSON')}</p>}\n </div>\n )}\n\n {hasTypedEditor && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n if (!jsonMode) {\n setJsonText(JSON.stringify(payload, null, 2))\n } else {\n try {\n setPayload(JSON.parse(jsonText))\n setJsonError(null)\n } catch {\n setJsonError(t('inbox_ops.edit_dialog.invalid_json', 'Invalid JSON'))\n return\n }\n }\n setJsonMode(!jsonMode)\n }}\n >\n {jsonMode ? t('inbox_ops.edit_dialog.form_view', 'Form view') : t('inbox_ops.edit_dialog.json_view', 'JSON view')}\n </Button>\n )}\n </div>\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose} disabled={isSaving}>\n {t('inbox_ops.edit_dialog.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSave} disabled={isSaving}>\n {isSaving && <Loader2 className=\"h-4 w-4 animate-spin mr-1\" />}\n {t('inbox_ops.edit_dialog.save', 'Save Changes')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { apiCallOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader, extractOptimisticLockConflict } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Loader2 } from 'lucide-react'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogFooter,\n DialogDescription,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport type { ActionDetail } from './types'\nimport { useActionDescriptionResolver } from './ActionCard'\n\nfunction ShipmentPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n const trackingNumbers = ((payload.trackingNumbers as string[]) || []).join(', ')\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.carrier', 'Carrier')}</Label>\n <Input value={(payload.carrierName as string) || ''} onChange={(event) => updateField('carrierName', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.status', 'Status')}</Label>\n <Input value={(payload.statusLabel as string) || ''} onChange={(event) => updateField('statusLabel', event.target.value)} />\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.tracking_numbers', 'Tracking Numbers')}</Label>\n <Input\n value={trackingNumbers}\n onChange={(event) => updateField('trackingNumbers', event.target.value.split(',').map((s) => s.trim()).filter(Boolean))}\n placeholder={t('inbox_ops.placeholder.comma_separated', 'Comma-separated')}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.shipped_at', 'Shipped At')}</Label>\n <Input value={(payload.shippedAt as string) || ''} onChange={(event) => updateField('shippedAt', event.target.value)} placeholder=\"YYYY-MM-DD\" />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.estimated_delivery', 'Estimated Delivery')}</Label>\n <Input value={(payload.estimatedDelivery as string) || ''} onChange={(event) => updateField('estimatedDelivery', event.target.value)} placeholder=\"YYYY-MM-DD\" />\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.notes', 'Notes')}</Label>\n <Textarea value={(payload.notes as string) || ''} onChange={(event) => updateField('notes', event.target.value)} rows={2} />\n </div>\n </div>\n )\n}\n\nfunction ContactPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n const type = (payload.type as string) || 'person'\n const isPerson = type === 'person'\n\n const existingName = (payload.name as string) || ''\n const nameParts = existingName.trim().split(/\\s+/).filter((p) => p.length > 0)\n const [firstName, setFirstName] = React.useState(() => isPerson ? (nameParts[0] || '') : '')\n const [lastName, setLastName] = React.useState(() => isPerson ? (nameParts.slice(1).join(' ') || '') : '')\n\n const updatePersonName = React.useCallback((first: string, last: string) => {\n const combined = `${first} ${last}`.trim()\n updateField('name', combined)\n }, [updateField])\n\n const lastNameMissing = isPerson && !lastName.trim()\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n {isPerson ? (\n <>\n <div>\n <Label>{t('inbox_ops.contact.first_name', 'First Name')}</Label>\n <Input\n value={firstName}\n onChange={(event) => {\n setFirstName(event.target.value)\n updatePersonName(event.target.value, lastName)\n }}\n />\n </div>\n <div>\n <Label>{t('inbox_ops.contact.last_name', 'Last Name')}</Label>\n <Input\n value={lastName}\n onChange={(event) => {\n setLastName(event.target.value)\n updatePersonName(firstName, event.target.value)\n }}\n className={lastNameMissing ? 'border-status-error-border' : ''}\n />\n {lastNameMissing && (\n <p className=\"text-xs text-status-error-text mt-1\">{t('inbox_ops.contact.last_name_required', 'Last name is required')}</p>\n )}\n </div>\n </>\n ) : (\n <div>\n <Label>{t('inbox_ops.edit_dialog.name', 'Name')}</Label>\n <Input value={existingName} onChange={(event) => updateField('name', event.target.value)} />\n </div>\n )}\n <div>\n <Label>{t('inbox_ops.edit_dialog.type', 'Type')}</Label>\n <Select\n value={type}\n onValueChange={(value) => updateField('type', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"person\">{t('inbox_ops.contact_type.person', 'Person')}</SelectItem>\n <SelectItem value=\"company\">{t('inbox_ops.contact_type.company', 'Company')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.email', 'Email')}</Label>\n <Input value={(payload.email as string) || ''} onChange={(event) => updateField('email', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.phone', 'Phone')}</Label>\n <Input value={(payload.phone as string) || ''} onChange={(event) => updateField('phone', event.target.value)} />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.company_name', 'Company Name')}</Label>\n <Input value={(payload.companyName as string) || ''} onChange={(event) => updateField('companyName', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.role', 'Role')}</Label>\n <Input value={(payload.role as string) || ''} onChange={(event) => updateField('role', event.target.value)} />\n </div>\n </div>\n </div>\n )\n}\n\nfunction LinkContactPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.email', 'Email')}</Label>\n <Input value={(payload.emailAddress as string) || ''} onChange={(event) => updateField('emailAddress', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_id', 'Contact ID')}</Label>\n <Input value={(payload.contactId as string) || ''} onChange={(event) => updateField('contactId', event.target.value)} />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_type', 'Contact Type')}</Label>\n <Select\n value={(payload.contactType as string) || 'person'}\n onValueChange={(value) => updateField('contactType', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"person\">{t('inbox_ops.contact_type.person', 'Person')}</SelectItem>\n <SelectItem value=\"company\">{t('inbox_ops.contact_type.company', 'Company')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_name', 'Contact Name')}</Label>\n <Input value={(payload.contactName as string) || ''} onChange={(event) => updateField('contactName', event.target.value)} />\n </div>\n </div>\n </div>\n )\n}\n\nfunction DraftReplyPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.to', 'To')}</Label>\n <Input value={(payload.to as string) || ''} onChange={(event) => updateField('to', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.to_name', 'To Name')}</Label>\n <Input value={(payload.toName as string) || ''} onChange={(event) => updateField('toName', event.target.value)} />\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.subject', 'Subject')}</Label>\n <Input value={(payload.subject as string) || ''} onChange={(event) => updateField('subject', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.body', 'Body')}</Label>\n <Textarea value={(payload.body as string) || ''} onChange={(event) => updateField('body', event.target.value)} rows={6} />\n </div>\n </div>\n )\n}\n\nfunction UpdateOrderPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n const quantityChanges = (payload.quantityChanges as Record<string, unknown>[]) || []\n const deliveryDateChange = (payload.deliveryDateChange as Record<string, unknown>) || {}\n const noteAdditions = Array.isArray(payload.noteAdditions) ? (payload.noteAdditions as string[]).join('\\n') : ''\n\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.order_id', 'Order ID')}</Label>\n <Input value={(payload.orderId as string) || ''} onChange={(event) => updateField('orderId', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.order_number', 'Order Number')}</Label>\n <Input value={(payload.orderNumber as string) || ''} onChange={(event) => updateField('orderNumber', event.target.value)} />\n </div>\n </div>\n\n {quantityChanges.length > 0 && (\n <div>\n <Label className=\"mb-1 block\">{t('inbox_ops.edit_dialog.quantity_changes', 'Quantity Changes')}</Label>\n <div className=\"space-y-2\">\n {quantityChanges.map((change, index) => (\n <div key={index} className=\"border rounded p-2 space-y-2\">\n <div className=\"grid grid-cols-3 gap-2\">\n <div>\n <Label className=\"text-xs\">{t('inbox_ops.edit_dialog.line_item', 'Line Item')}</Label>\n <Input\n className=\"h-8 text-sm\"\n value={(change.lineItemName as string) || ''}\n onChange={(event) => {\n const arr = [...quantityChanges]\n arr[index] = { ...arr[index], lineItemName: event.target.value }\n updateField('quantityChanges', arr)\n }}\n />\n </div>\n <div>\n <Label className=\"text-xs\">{t('inbox_ops.edit_dialog.old_qty', 'Old Qty')}</Label>\n <Input\n className=\"h-8 text-sm\"\n value={(change.oldQuantity as string) || ''}\n onChange={(event) => {\n const arr = [...quantityChanges]\n arr[index] = { ...arr[index], oldQuantity: event.target.value }\n updateField('quantityChanges', arr)\n }}\n />\n </div>\n <div>\n <Label className=\"text-xs\">{t('inbox_ops.edit_dialog.new_qty', 'New Qty')}</Label>\n <Input\n className=\"h-8 text-sm\"\n value={(change.newQuantity as string) || ''}\n onChange={(event) => {\n const arr = [...quantityChanges]\n arr[index] = { ...arr[index], newQuantity: event.target.value }\n updateField('quantityChanges', arr)\n }}\n />\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {Boolean(deliveryDateChange.oldDate || deliveryDateChange.newDate) && (\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.old_delivery_date', 'Old Delivery Date')}</Label>\n <Input\n value={(deliveryDateChange.oldDate as string) || ''}\n onChange={(event) => updateField('deliveryDateChange', { ...deliveryDateChange, oldDate: event.target.value })}\n placeholder=\"YYYY-MM-DD\"\n />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.new_delivery_date', 'New Delivery Date')}</Label>\n <Input\n value={(deliveryDateChange.newDate as string) || ''}\n onChange={(event) => updateField('deliveryDateChange', { ...deliveryDateChange, newDate: event.target.value })}\n placeholder=\"YYYY-MM-DD\"\n />\n </div>\n </div>\n )}\n\n <div>\n <Label>{t('inbox_ops.edit_dialog.note_additions', 'Notes')}</Label>\n <Textarea\n value={noteAdditions}\n onChange={(event) => {\n const lines = event.target.value.split('\\n').filter(Boolean)\n updateField('noteAdditions', lines.length > 0 ? lines : undefined)\n }}\n rows={2}\n placeholder={t('inbox_ops.placeholder.one_note_per_line', 'One note per line')}\n />\n </div>\n </div>\n )\n}\n\nfunction LogActivityPayloadEditor({\n payload,\n updateField,\n}: {\n payload: Record<string, unknown>\n updateField: (key: string, value: unknown) => void\n}) {\n const t = useT()\n return (\n <div className=\"space-y-3\">\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_name', 'Contact Name')}</Label>\n <Input value={(payload.contactName as string) || ''} onChange={(event) => updateField('contactName', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.contact_type', 'Contact Type')}</Label>\n <Select\n value={(payload.contactType as string) || 'person'}\n onValueChange={(value) => updateField('contactType', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"person\">{t('inbox_ops.contact_type.person', 'Person')}</SelectItem>\n <SelectItem value=\"company\">{t('inbox_ops.contact_type.company', 'Company')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.activity_type', 'Activity Type')}</Label>\n <Select\n value={(payload.activityType as string) || 'email'}\n onValueChange={(value) => updateField('activityType', value)}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"email\">{t('inbox_ops.activity_type.email', 'Email')}</SelectItem>\n <SelectItem value=\"call\">{t('inbox_ops.activity_type.call', 'Call')}</SelectItem>\n <SelectItem value=\"meeting\">{t('inbox_ops.activity_type.meeting', 'Meeting')}</SelectItem>\n <SelectItem value=\"note\">{t('inbox_ops.activity_type.note', 'Note')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.subject', 'Subject')}</Label>\n <Input value={(payload.subject as string) || ''} onChange={(event) => updateField('subject', event.target.value)} />\n </div>\n <div>\n <Label>{t('inbox_ops.edit_dialog.body', 'Body')}</Label>\n <Textarea value={(payload.body as string) || ''} onChange={(event) => updateField('body', event.target.value)} rows={6} />\n </div>\n </div>\n )\n}\n\nexport function EditActionDialog({\n action,\n actionTypeLabels,\n onClose,\n onSaved,\n}: {\n action: ActionDetail\n actionTypeLabels: Record<string, string>\n onClose: () => void\n onSaved: () => void\n}) {\n const t = useT()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'inbox-ops-edit-action',\n })\n const [payload, setPayload] = React.useState<Record<string, unknown>>(\n () => structuredClone(action.payload),\n )\n const [isSaving, setIsSaving] = React.useState(false)\n const [jsonMode, setJsonMode] = React.useState(false)\n const [jsonText, setJsonText] = React.useState(() => JSON.stringify(action.payload, null, 2))\n const [jsonError, setJsonError] = React.useState<string | null>(null)\n\n const handleSave = React.useCallback(async () => {\n let finalPayload = payload\n if (jsonMode) {\n try {\n finalPayload = JSON.parse(jsonText)\n setJsonError(null)\n } catch {\n setJsonError(t('inbox_ops.edit_dialog.invalid_json', 'Invalid JSON'))\n return\n }\n }\n\n setIsSaving(true)\n try {\n await runMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(action.updatedAt),\n () => apiCallOrThrow<{ ok: boolean; error?: string }>(\n `/api/inbox_ops/proposals/${action.proposalId}/actions/${action.id}`,\n { method: 'PATCH', body: JSON.stringify({ payload: finalPayload }) },\n ),\n ),\n context: {},\n })\n flash(t('inbox_ops.edit_dialog.saved', 'Action updated successfully'), 'success')\n onSaved()\n onClose()\n } catch (error) {\n if (extractOptimisticLockConflict(error)) {\n onClose()\n return\n }\n const message = error instanceof Error ? error.message.trim() : ''\n flash(message || t('inbox_ops.flash.save_failed', 'Failed to save'), 'error')\n } finally {\n setIsSaving(false)\n }\n }, [action, payload, jsonMode, jsonText, t, onSaved, onClose, runMutation])\n\n React.useEffect(() => {\n function handleKeyDown(event: KeyboardEvent) {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleSave()\n }\n }\n window.addEventListener('keydown', handleKeyDown)\n return () => window.removeEventListener('keydown', handleKeyDown)\n }, [handleSave])\n\n const updateField = (key: string, value: unknown) => {\n setPayload((prev) => ({ ...prev, [key]: value }))\n }\n\n const resolveActionDescription = useActionDescriptionResolver()\n const label = actionTypeLabels[action.actionType] || action.actionType\n const hasTypedEditor = [\n 'create_order', 'create_quote', 'update_order', 'update_shipment', 'create_contact', 'link_contact', 'log_activity', 'draft_reply',\n ].includes(action.actionType)\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose() }}>\n <DialogContent className=\"sm:max-w-2xl sm:max-h-[85vh]\">\n <DialogHeader>\n <DialogTitle>{t('inbox_ops.edit_dialog.title', 'Edit Action')}: {label}</DialogTitle>\n <DialogDescription>{resolveActionDescription(action.description, action.payload)}</DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 overflow-y-auto max-h-[60vh] py-2\">\n {hasTypedEditor && !jsonMode && (\n <>\n {action.actionType === 'update_order' && (\n <UpdateOrderPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'update_shipment' && (\n <ShipmentPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'create_contact' && (\n <ContactPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'link_contact' && (\n <LinkContactPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'log_activity' && (\n <LogActivityPayloadEditor payload={payload} updateField={updateField} />\n )}\n {action.actionType === 'draft_reply' && (\n <DraftReplyPayloadEditor payload={payload} updateField={updateField} />\n )}\n </>\n )}\n\n {(!hasTypedEditor || jsonMode) && (\n <div className=\"space-y-2\">\n <Label>{t('inbox_ops.edit_dialog.payload_json', 'Payload (JSON)')}</Label>\n <Textarea\n className=\"font-mono text-xs min-h-[200px]\"\n value={jsonText}\n onChange={(event) => {\n setJsonText(event.target.value)\n setJsonError(null)\n }}\n />\n {jsonError && <p className=\"text-xs text-status-error-text\">{t('inbox_ops.edit_dialog.invalid_json', 'Invalid JSON')}</p>}\n </div>\n )}\n\n {hasTypedEditor && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n if (!jsonMode) {\n setJsonText(JSON.stringify(payload, null, 2))\n } else {\n try {\n setPayload(JSON.parse(jsonText))\n setJsonError(null)\n } catch {\n setJsonError(t('inbox_ops.edit_dialog.invalid_json', 'Invalid JSON'))\n return\n }\n }\n setJsonMode(!jsonMode)\n }}\n >\n {jsonMode ? t('inbox_ops.edit_dialog.form_view', 'Form view') : t('inbox_ops.edit_dialog.json_view', 'JSON view')}\n </Button>\n )}\n </div>\n\n <DialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose} disabled={isSaving}>\n {t('inbox_ops.edit_dialog.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSave} disabled={isSaving}>\n {isSaving && <Loader2 className=\"h-4 w-4 animate-spin mr-1\" />}\n {t('inbox_ops.edit_dialog.save', 'Save Changes')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -76,6 +76,16 @@ async function POST(req) {
76
76
  }
77
77
  }
78
78
  ).catch(() => void 0);
79
+ if (force && partitions.length > 1) {
80
+ try {
81
+ await bus.emitEvent(
82
+ "query_index.vectorize_purge",
83
+ { entityType, tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },
84
+ { persistent: true, deliverInline: false }
85
+ );
86
+ } catch {
87
+ }
88
+ }
79
89
  try {
80
90
  await Promise.all(
81
91
  partitions.map((part) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/query_index/api/reindex.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexReindexRequestSchema } from './openapi'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { isValidEntityIdShape } from '@open-mercato/shared/lib/query/engine'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['query_index.reindex'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const body = await req.json().catch(() => ({})) as any\n const entityType = String(body?.entityType || '')\n if (!entityType) return NextResponse.json({ error: 'Missing entityType' }, { status: 400 })\n if (!isValidEntityIdShape(entityType)) {\n return NextResponse.json({ error: 'Invalid entityType' }, { status: 400 })\n }\n const force = Boolean(body?.force)\n const batchSize = Number.isFinite(body?.batchSize) ? Math.max(1, Math.trunc(body.batchSize)) : undefined\n const partitionCountInput = Number(body?.partitionCount)\n const partitionCount = Number.isFinite(partitionCountInput)\n ? Math.max(1, Math.trunc(partitionCountInput))\n : 1\n const partitionIndexInput = Number(body?.partitionIndex)\n const partitionIndex = Number.isFinite(partitionIndexInput) ? Math.max(0, Math.trunc(partitionIndexInput)) : undefined\n if (partitionIndex !== undefined && partitionIndex >= partitionCount) {\n return NextResponse.json({ error: 'partitionIndex must be < partitionCount' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n let em: any | null = null\n try {\n em = container.resolve('em')\n } catch {}\n const bus = container.resolve('eventBus') as any\n const partitions = partitionIndex !== undefined\n ? [partitionIndex]\n : Array.from({ length: partitionCount }, (_, idx) => idx)\n const firstPartition = partitions[0] ?? 0\n\n const guardUserId = typeof auth.sub === 'string' ? auth.sub : ''\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: {\n entityType,\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n message: `Reindex requested for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n },\n ).catch(() => undefined)\n try {\n await Promise.all(\n partitions.map((part) => {\n const payload: Record<string, unknown> = {\n entityType,\n force,\n batchSize,\n partitionCount,\n partitionIndex: part,\n resetCoverage: part === firstPartition,\n }\n if (auth.tenantId !== undefined) {\n payload.tenantId = auth.tenantId ?? null\n }\n if (auth.orgId !== undefined) {\n payload.organizationId = auth.orgId ?? null\n }\n if (typeof auth.sub === 'string' && auth.sub.length > 0) {\n payload.requestedByUserId = auth.sub\n }\n return bus.emitEvent(\n 'query_index.reindex',\n payload,\n { persistent: true, deliverInline: false },\n )\n }),\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n message: `Reindex queued for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n },\n ).catch(() => undefined)\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n } catch (error) {\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n level: 'warn',\n message: `Failed to queue reindex for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n error: error instanceof Error ? error.message : String(error),\n },\n },\n ).catch(() => undefined)\n throw error\n }\n return NextResponse.json({ ok: true })\n}\n\nconst queryIndexReindexDoc: OpenApiMethodDoc = {\n summary: 'Trigger query index rebuild',\n description: 'Queues a reindex job for the specified entity type within the current tenant scope.',\n tags: [queryIndexTag],\n requestBody: {\n contentType: 'application/json',\n schema: queryIndexReindexRequestSchema,\n description: 'Entity identifier and optional force flag.',\n },\n responses: [\n { status: 200, description: 'Reindex job accepted.', schema: queryIndexOkSchema },\n ],\n errors: [\n { status: 400, description: 'Missing entity type', schema: queryIndexErrorSchema },\n { status: 401, description: 'Authentication required', schema: queryIndexErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: queryIndexTag,\n summary: 'Queue a query index rebuild',\n methods: {\n POST: queryIndexReindexDoc,\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { queryIndexTag, queryIndexErrorSchema, queryIndexOkSchema, queryIndexReindexRequestSchema } from './openapi'\nimport { recordIndexerLog } from '@open-mercato/shared/lib/indexers/status-log'\nimport { isValidEntityIdShape } from '@open-mercato/shared/lib/query/engine'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['query_index.reindex'] },\n}\n\nexport async function POST(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const body = await req.json().catch(() => ({})) as any\n const entityType = String(body?.entityType || '')\n if (!entityType) return NextResponse.json({ error: 'Missing entityType' }, { status: 400 })\n if (!isValidEntityIdShape(entityType)) {\n return NextResponse.json({ error: 'Invalid entityType' }, { status: 400 })\n }\n const force = Boolean(body?.force)\n const batchSize = Number.isFinite(body?.batchSize) ? Math.max(1, Math.trunc(body.batchSize)) : undefined\n const partitionCountInput = Number(body?.partitionCount)\n const partitionCount = Number.isFinite(partitionCountInput)\n ? Math.max(1, Math.trunc(partitionCountInput))\n : 1\n const partitionIndexInput = Number(body?.partitionIndex)\n const partitionIndex = Number.isFinite(partitionIndexInput) ? Math.max(0, Math.trunc(partitionIndexInput)) : undefined\n if (partitionIndex !== undefined && partitionIndex >= partitionCount) {\n return NextResponse.json({ error: 'partitionIndex must be < partitionCount' }, { status: 400 })\n }\n\n const container = await createRequestContainer()\n let em: any | null = null\n try {\n em = container.resolve('em')\n } catch {}\n const bus = container.resolve('eventBus') as any\n const partitions = partitionIndex !== undefined\n ? [partitionIndex]\n : Array.from({ length: partitionCount }, (_, idx) => idx)\n const firstPartition = partitions[0] ?? 0\n\n const guardUserId = typeof auth.sub === 'string' ? auth.sub : ''\n const guardResult = await validateCrudMutationGuard(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: {\n entityType,\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n message: `Reindex requested for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n },\n ).catch(() => undefined)\n // Queued once here rather than from a partition: the purge deletes every vector for the\n // scope, so emitting it from inside the fan-out would race the per-record vectorize jobs\n // its siblings are already queueing and drop vectors this run had just rebuilt.\n if (force && partitions.length > 1) {\n try {\n await bus.emitEvent(\n 'query_index.vectorize_purge',\n { entityType, tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n { persistent: true, deliverInline: false },\n )\n } catch {\n // Best effort: a failed purge leaves stale vectors, which the orphan sweep still prunes.\n }\n }\n\n try {\n await Promise.all(\n partitions.map((part) => {\n const payload: Record<string, unknown> = {\n entityType,\n force,\n batchSize,\n partitionCount,\n partitionIndex: part,\n resetCoverage: part === firstPartition,\n }\n if (auth.tenantId !== undefined) {\n payload.tenantId = auth.tenantId ?? null\n }\n if (auth.orgId !== undefined) {\n payload.organizationId = auth.orgId ?? null\n }\n if (typeof auth.sub === 'string' && auth.sub.length > 0) {\n payload.requestedByUserId = auth.sub\n }\n return bus.emitEvent(\n 'query_index.reindex',\n payload,\n { persistent: true, deliverInline: false },\n )\n }),\n )\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n message: `Reindex queued for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n force,\n batchSize: batchSize ?? null,\n partitionCount,\n partitionIndex: partitionIndex ?? null,\n },\n },\n ).catch(() => undefined)\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(container, {\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n userId: guardUserId,\n resourceKind: 'query_index',\n resourceId: entityType,\n operation: 'custom',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n } catch (error) {\n await recordIndexerLog(\n { em: em ?? undefined },\n {\n source: 'query_index',\n handler: 'api:query_index.reindex',\n level: 'warn',\n message: `Failed to queue reindex for ${entityType}`,\n entityType,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n details: {\n error: error instanceof Error ? error.message : String(error),\n },\n },\n ).catch(() => undefined)\n throw error\n }\n return NextResponse.json({ ok: true })\n}\n\nconst queryIndexReindexDoc: OpenApiMethodDoc = {\n summary: 'Trigger query index rebuild',\n description: 'Queues a reindex job for the specified entity type within the current tenant scope.',\n tags: [queryIndexTag],\n requestBody: {\n contentType: 'application/json',\n schema: queryIndexReindexRequestSchema,\n description: 'Entity identifier and optional force flag.',\n },\n responses: [\n { status: 200, description: 'Reindex job accepted.', schema: queryIndexOkSchema },\n ],\n errors: [\n { status: 400, description: 'Missing entity type', schema: queryIndexErrorSchema },\n { status: 401, description: 'Authentication required', schema: queryIndexErrorSchema },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: queryIndexTag,\n summary: 'Queue a query index rebuild',\n methods: {\n POST: queryIndexReindexDoc,\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import { createProgressBar } from "@open-mercato/shared/lib/cli/progress";
4
4
  import { resolveTenantEncryptionService } from "@open-mercato/shared/lib/encryption/customFieldValues";
5
5
  import { decryptIndexDocForSearch, encryptIndexDocForStorage } from "@open-mercato/shared/lib/encryption/indexDoc";
6
6
  import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
7
- import { resolveEntityTableName } from "@open-mercato/shared/lib/query/engine";
7
+ import { resolveEntityTableName, resolveRegisteredEntityTableName } from "@open-mercato/shared/lib/query/engine";
8
8
  import { recordIndexerError } from "@open-mercato/shared/lib/indexers/error-log";
9
9
  import { recordIndexerLog } from "@open-mercato/shared/lib/indexers/status-log";
10
10
  import {
@@ -15,6 +15,7 @@ import {
15
15
  } from "./lib/batch.js";
16
16
  import { reindexEntity, DEFAULT_REINDEX_PARTITIONS } from "./lib/reindexer.js";
17
17
  import { purgeIndexScope } from "./lib/purge.js";
18
+ import { refreshCoverageSnapshot } from "./lib/coverage.js";
18
19
  import { flattenSystemEntityIds } from "@open-mercato/shared/lib/entities/system-entities";
19
20
  function isIndexerVerbose() {
20
21
  const parsed = parseBooleanToken(process.env.OM_INDEXER_VERBOSE ?? "");
@@ -243,6 +244,113 @@ function describeScope(scope) {
243
244
  if (!scope.includeDeleted && scope.supportsDeleted) parts.push("active-only");
244
245
  return parts.length ? ` (${parts.join(" ")})` : "";
245
246
  }
247
+ const MAX_TARGETED_REPAIR_RECORDS = 5e3;
248
+ async function findUnindexedRecordIds(db, options) {
249
+ const columns = await getColumnSet(db, options.tableName);
250
+ let query = db.selectFrom(`${options.tableName} as b`).select("b.id").where(
251
+ ({ not, exists, selectFrom }) => not(exists(
252
+ selectFrom("entity_indexes as ei").select(sql`1`.as("one")).whereRef("ei.entity_id", "=", sql`b.id::text`).where("ei.entity_type", "=", options.entityType).where("ei.deleted_at", "is", null)
253
+ ))
254
+ ).limit(options.limit);
255
+ if (columns.has("deleted_at")) query = query.where("b.deleted_at", "is", null);
256
+ if (options.tenantId != null && columns.has("tenant_id")) {
257
+ query = query.where("b.tenant_id", "=", options.tenantId);
258
+ }
259
+ if (options.organizationId != null && columns.has("organization_id")) {
260
+ query = query.where("b.organization_id", "=", options.organizationId);
261
+ }
262
+ const rows = await query.execute();
263
+ return rows.map((row) => String(row.id));
264
+ }
265
+ async function verifyAndRepairIndexCoverage(em, options) {
266
+ const scope = {
267
+ entityType: options.entityType,
268
+ tenantId: options.tenantId ?? null,
269
+ organizationId: options.organizationId ?? null,
270
+ withDeleted: false
271
+ };
272
+ let counts = null;
273
+ try {
274
+ counts = await refreshCoverageSnapshot(em, scope);
275
+ } catch (error) {
276
+ console.warn(
277
+ ` -> ${options.entityType}: coverage verification skipped (${error instanceof Error ? error.message : String(error)})`
278
+ );
279
+ return;
280
+ }
281
+ if (!counts) return;
282
+ if (counts.indexedCount > counts.baseCount) {
283
+ console.warn(
284
+ ` -> ${options.entityType}: ${counts.indexedCount} index rows for ${counts.baseCount} records \u2014 not repairable by reindexing`
285
+ );
286
+ return;
287
+ }
288
+ if (counts.indexedCount === counts.baseCount) return;
289
+ const db = em.getKysely();
290
+ let tableName = null;
291
+ try {
292
+ tableName = resolveRegisteredEntityTableName(em, options.entityType);
293
+ } catch {
294
+ tableName = null;
295
+ }
296
+ const missingIds = tableName ? await findUnindexedRecordIds(db, {
297
+ entityType: options.entityType,
298
+ tableName,
299
+ tenantId: options.tenantId ?? null,
300
+ organizationId: options.organizationId ?? null,
301
+ limit: MAX_TARGETED_REPAIR_RECORDS + 1
302
+ }).catch(() => null) : null;
303
+ const targeted = missingIds && missingIds.length > 0 && missingIds.length <= MAX_TARGETED_REPAIR_RECORDS;
304
+ console.log(
305
+ ` -> ${options.entityType}: ${counts.indexedCount}/${counts.baseCount} indexed after reindex \u2014 repairing${targeted ? ` ${missingIds.length} missing record(s)` : " the whole entity"}...`
306
+ );
307
+ try {
308
+ if (targeted) {
309
+ const columns = await getColumnSet(db, tableName);
310
+ for (const recordId of missingIds) {
311
+ await rebuildEntityIndexes({
312
+ em,
313
+ db,
314
+ entityType: options.entityType,
315
+ tableName,
316
+ orgOverride: options.organizationId ?? void 0,
317
+ tenantOverride: options.tenantId ?? void 0,
318
+ global: false,
319
+ includeDeleted: false,
320
+ offset: 0,
321
+ recordId,
322
+ batchSize: 1,
323
+ supportsOrgFilter: columns.has("organization_id"),
324
+ supportsTenantFilter: columns.has("tenant_id"),
325
+ supportsDeletedFilter: columns.has("deleted_at")
326
+ });
327
+ }
328
+ } else {
329
+ await reindexEntity(em, {
330
+ entityType: options.entityType,
331
+ tenantId: options.tenantId,
332
+ organizationId: options.organizationId,
333
+ force: false,
334
+ batchSize: options.batchSize,
335
+ emitVectorizeEvents: false,
336
+ resetCoverage: false
337
+ });
338
+ }
339
+ } catch (error) {
340
+ console.warn(
341
+ ` -> ${options.entityType}: repair pass failed (${error instanceof Error ? error.message : String(error)})`
342
+ );
343
+ return;
344
+ }
345
+ const after = await refreshCoverageSnapshot(em, scope).catch(() => null);
346
+ if (after && after.indexedCount < after.baseCount) {
347
+ console.warn(
348
+ ` -> ${options.entityType}: still ${after.indexedCount}/${after.baseCount} indexed after repair`
349
+ );
350
+ } else {
351
+ console.log(` -> ${options.entityType}: coverage repaired`);
352
+ }
353
+ }
246
354
  const rebuild = {
247
355
  command: "rebuild",
248
356
  async run(rest) {
@@ -583,6 +691,14 @@ const reindex = {
583
691
  groupedProgress?.complete();
584
692
  const totalProcessed = stats.reduce((acc, value) => acc + value, 0);
585
693
  console.log(`Finished ${entity}: processed ${totalProcessed} row(s) across ${partitionTargets.length} partition(s)`);
694
+ if (partitionIndexOption === void 0) {
695
+ await verifyAndRepairIndexCoverage(baseEm, {
696
+ entityType: entity,
697
+ tenantId,
698
+ organizationId: orgId,
699
+ batchSize
700
+ });
701
+ }
586
702
  await recordIndexerLog(
587
703
  { em: baseEm },
588
704
  {
@@ -716,6 +832,14 @@ const reindex = {
716
832
  groupedProgress?.complete();
717
833
  const totalProcessed = partitionResults.reduce((acc, value) => acc + value, 0);
718
834
  console.log(` -> ${id} complete: processed ${totalProcessed} row(s) across ${partitionTargets.length} partition(s)`);
835
+ if (partitionIndexOption === void 0) {
836
+ await verifyAndRepairIndexCoverage(baseEm, {
837
+ entityType: id,
838
+ tenantId,
839
+ organizationId: orgId,
840
+ batchSize
841
+ });
842
+ }
719
843
  await recordIndexerLog(
720
844
  { em: baseEm },
721
845
  {