@open-mercato/core 0.4.5-develop-811deeb983 → 0.4.5-develop-3d8e759e45

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 (76) hide show
  1. package/dist/modules/catalog/inbox-actions.js +51 -0
  2. package/dist/modules/catalog/inbox-actions.js.map +7 -0
  3. package/dist/modules/customers/inbox-actions.js +230 -0
  4. package/dist/modules/customers/inbox-actions.js.map +7 -0
  5. package/dist/modules/inbox_ops/api/emails/[id]/route.js +40 -1
  6. package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
  7. package/dist/modules/inbox_ops/api/extract/route.js +87 -0
  8. package/dist/modules/inbox_ops/api/extract/route.js.map +7 -0
  9. package/dist/modules/inbox_ops/api/proposals/[id]/translate/route.js +6 -1
  10. package/dist/modules/inbox_ops/api/proposals/[id]/translate/route.js.map +2 -2
  11. package/dist/modules/inbox_ops/api/proposals/counts/route.js.map +2 -2
  12. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +40 -14
  13. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
  14. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.meta.js +2 -2
  15. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.meta.js.map +2 -2
  16. package/dist/modules/inbox_ops/backend/inbox-ops/page.js +161 -79
  17. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
  18. package/dist/modules/inbox_ops/backend/inbox-ops/page.meta.js +2 -2
  19. package/dist/modules/inbox_ops/backend/inbox-ops/page.meta.js.map +2 -2
  20. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js +109 -62
  21. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js.map +3 -3
  22. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.js +2 -2
  23. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.js.map +2 -2
  24. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +36 -14
  25. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
  26. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.meta.js +2 -2
  27. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.meta.js.map +2 -2
  28. package/dist/modules/inbox_ops/components/proposals/ActionCard.js +65 -10
  29. package/dist/modules/inbox_ops/components/proposals/ActionCard.js.map +2 -2
  30. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +58 -10
  31. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  32. package/dist/modules/inbox_ops/lib/constants.js.map +2 -2
  33. package/dist/modules/inbox_ops/lib/contactValidation.js +40 -0
  34. package/dist/modules/inbox_ops/lib/contactValidation.js.map +7 -0
  35. package/dist/modules/inbox_ops/lib/executionEngine.js +31 -826
  36. package/dist/modules/inbox_ops/lib/executionEngine.js.map +3 -3
  37. package/dist/modules/inbox_ops/lib/executionHelpers.js +368 -0
  38. package/dist/modules/inbox_ops/lib/executionHelpers.js.map +7 -0
  39. package/dist/modules/inbox_ops/lib/extractionPrompt.js +28 -35
  40. package/dist/modules/inbox_ops/lib/extractionPrompt.js.map +3 -3
  41. package/dist/modules/inbox_ops/lib/inbox-actions-generated.d.js +1 -0
  42. package/dist/modules/inbox_ops/lib/inbox-actions-generated.d.js.map +7 -0
  43. package/dist/modules/inbox_ops/lib/translationProvider.js +15 -10
  44. package/dist/modules/inbox_ops/lib/translationProvider.js.map +2 -2
  45. package/dist/modules/inbox_ops/subscribers/extractionWorker.js +16 -16
  46. package/dist/modules/inbox_ops/subscribers/extractionWorker.js.map +2 -2
  47. package/dist/modules/sales/inbox-actions.js +278 -0
  48. package/dist/modules/sales/inbox-actions.js.map +7 -0
  49. package/jest.config.cjs +1 -0
  50. package/jest.mocks/inbox-actions.generated.js +5 -0
  51. package/package.json +2 -2
  52. package/src/modules/catalog/inbox-actions.ts +60 -0
  53. package/src/modules/customers/inbox-actions.ts +285 -0
  54. package/src/modules/inbox_ops/api/emails/[id]/route.ts +44 -0
  55. package/src/modules/inbox_ops/api/extract/route.ts +94 -0
  56. package/src/modules/inbox_ops/api/proposals/[id]/translate/route.ts +6 -1
  57. package/src/modules/inbox_ops/api/proposals/counts/route.ts +2 -0
  58. package/src/modules/inbox_ops/backend/inbox-ops/log/page.meta.ts +2 -2
  59. package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +43 -13
  60. package/src/modules/inbox_ops/backend/inbox-ops/page.meta.ts +2 -2
  61. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +176 -81
  62. package/src/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.ts +2 -2
  63. package/src/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.tsx +122 -68
  64. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.meta.ts +2 -2
  65. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +36 -14
  66. package/src/modules/inbox_ops/components/proposals/ActionCard.tsx +91 -7
  67. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +64 -12
  68. package/src/modules/inbox_ops/lib/constants.ts +9 -0
  69. package/src/modules/inbox_ops/lib/contactValidation.ts +54 -0
  70. package/src/modules/inbox_ops/lib/executionEngine.ts +47 -1060
  71. package/src/modules/inbox_ops/lib/executionHelpers.ts +527 -0
  72. package/src/modules/inbox_ops/lib/extractionPrompt.ts +45 -34
  73. package/src/modules/inbox_ops/lib/inbox-actions-generated.d.ts +11 -0
  74. package/src/modules/inbox_ops/lib/translationProvider.ts +16 -10
  75. package/src/modules/inbox_ops/subscribers/extractionWorker.ts +16 -18
  76. package/src/modules/sales/inbox-actions.ts +359 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/inbox_ops/backend/inbox-ops/proposals/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport {\n ArrowLeft,\n CheckCircle,\n XCircle,\n AlertTriangle,\n CheckCheck,\n Loader2,\n ExternalLink,\n RefreshCw,\n Users,\n Languages,\n} from 'lucide-react'\nimport type { ProposalTranslationEntry } from '../../../../data/entities'\nimport type { ProposalDetail, ActionDetail, DiscrepancyDetail, EmailDetail } from '../../../../components/proposals/types'\nimport { ActionCard, ConfidenceBadge, useActionTypeLabels } from '../../../../components/proposals/ActionCard'\nimport { EditActionDialog } from '../../../../components/proposals/EditActionDialog'\n\nfunction EmailThreadViewer({ email }: { email: EmailDetail | null }) {\n const t = useT()\n if (!email) return null\n\n const messages = email.threadMessages || []\n\n return (\n <div className=\"space-y-3\">\n <h3 className=\"font-semibold text-sm\">{t('inbox_ops.email_thread', 'Email Thread')}</h3>\n {messages.length > 0 ? (\n messages.map((msg, index) => (\n <div key={index} className=\"border rounded-lg p-3 md:p-4 bg-card\">\n <div className=\"flex items-center gap-2 mb-2\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium truncate\">\n {msg.from?.name || msg.from?.email || t('inbox_ops.sender_unknown', 'Unknown')}\n </div>\n <div className=\"text-xs text-muted-foreground truncate\">{msg.from?.email}</div>\n </div>\n <span className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {msg.date ? new Date(msg.date).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : ''}\n </span>\n </div>\n <div className=\"text-sm whitespace-pre-wrap text-foreground/80\">{msg.body}</div>\n </div>\n ))\n ) : email.cleanedText ? (\n <div className=\"border rounded-lg p-3 md:p-4 bg-card\">\n <div className=\"text-sm whitespace-pre-wrap text-foreground/80\">{email.cleanedText}</div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.no_email_content', 'No email content available')}</p>\n )}\n </div>\n )\n}\n\nexport default function ProposalDetailPage({ params }: { params?: { id?: string } }) {\n const t = useT()\n const locale = useLocale()\n const router = useRouter()\n const proposalId = params?.id\n\n const [proposal, setProposal] = React.useState<ProposalDetail | null>(null)\n const [actions, setActions] = React.useState<ActionDetail[]>([])\n const [discrepancies, setDiscrepancies] = React.useState<DiscrepancyDetail[]>([])\n const [email, setEmail] = React.useState<EmailDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [isProcessing, setIsProcessing] = React.useState(false)\n\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const actionTypeLabels = useActionTypeLabels()\n const [editingAction, setEditingAction] = React.useState<ActionDetail | null>(null)\n const [sendingReplyId, setSendingReplyId] = React.useState<string | null>(null)\n\n const [translation, setTranslation] = React.useState<ProposalTranslationEntry | null>(null)\n const [isTranslating, setIsTranslating] = React.useState(false)\n const [showTranslation, setShowTranslation] = React.useState(false)\n\n const handleEditAction = React.useCallback((action: ActionDetail) => {\n if (action.actionType === 'create_order' || action.actionType === 'create_quote') {\n const kind = action.actionType === 'create_order' ? 'order' : 'quote'\n try {\n sessionStorage.setItem(\n 'inbox_ops.orderDraft',\n JSON.stringify({\n actionId: action.id,\n proposalId: action.proposalId,\n payload: action.payload,\n }),\n )\n } catch { /* sessionStorage unavailable */ }\n router.push(`/backend/sales/documents/create?kind=${kind}&fromInboxAction=${encodeURIComponent(action.id)}`)\n return\n }\n if (action.actionType === 'create_product') {\n try {\n sessionStorage.setItem(\n 'inbox_ops.productDraft',\n JSON.stringify({\n actionId: action.id,\n proposalId: action.proposalId,\n payload: action.payload,\n }),\n )\n } catch { /* sessionStorage unavailable */ }\n router.push(`/backend/catalog/products/create?fromInboxAction=${encodeURIComponent(action.id)}`)\n return\n }\n setEditingAction(action)\n }, [router])\n\n const handleTranslate = React.useCallback(async () => {\n if (!proposalId) return\n setIsTranslating(true)\n const result = await apiCall<{ translation: ProposalTranslationEntry; cached: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/translate`,\n { method: 'POST', body: JSON.stringify({ targetLocale: locale }) },\n )\n if (result?.ok && result.result?.translation) {\n setTranslation(result.result.translation)\n setShowTranslation(true)\n } else {\n flash(t('inbox_ops.translate.failed', 'Translation failed'), 'error')\n }\n setIsTranslating(false)\n }, [proposalId, locale, t])\n\n const loadData = React.useCallback(async () => {\n if (!proposalId) return\n setIsLoading(true)\n const result = await apiCall<{\n proposal: ProposalDetail\n actions: ActionDetail[]\n discrepancies: DiscrepancyDetail[]\n email: EmailDetail\n }>(`/api/inbox_ops/proposals/${proposalId}`)\n if (result?.ok && result.result) {\n setProposal(result.result.proposal)\n setActions(result.result.actions || [])\n setDiscrepancies(result.result.discrepancies || [])\n setEmail(result.result.email)\n }\n setIsLoading(false)\n }, [proposalId])\n\n React.useEffect(() => { loadData() }, [loadData])\n\n const handleAcceptAction = React.useCallback(async (actionId: string) => {\n setIsProcessing(true)\n const result = await apiCall<{ ok: boolean; error?: string }>(\n `/api/inbox_ops/proposals/${proposalId}/actions/${actionId}/accept`,\n { method: 'POST' },\n )\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.flash.action_executed', 'Action executed'), 'success')\n await loadData()\n } else {\n flash(result?.result?.error || t('inbox_ops.flash.action_execute_failed', 'Failed to execute action'), 'error')\n }\n setIsProcessing(false)\n }, [proposalId, loadData, t])\n\n const handleRejectAction = React.useCallback(async (actionId: string) => {\n setIsProcessing(true)\n const result = await apiCall<{ ok: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/actions/${actionId}/reject`,\n { method: 'POST' },\n )\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.flash.action_rejected', 'Action rejected'), 'success')\n await loadData()\n } else {\n flash(t('inbox_ops.flash.action_reject_failed', 'Failed to reject action'), 'error')\n }\n setIsProcessing(false)\n }, [proposalId, loadData])\n\n const handleAcceptAll = React.useCallback(async () => {\n const pendingCount = actions.filter((a) => a.status === 'pending').length\n const confirmed = await confirm({\n title: t('inbox_ops.action.accept_all', 'Accept All'),\n text: t('inbox_ops.action.accept_all_confirm', `Execute ${pendingCount} pending actions?`).replace('{count}', String(pendingCount)),\n })\n if (!confirmed) return\n\n setIsProcessing(true)\n const result = await apiCall<{ ok: boolean; succeeded: number; failed: number }>(\n `/api/inbox_ops/proposals/${proposalId}/accept-all`,\n { method: 'POST' },\n )\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.flash.accept_all_success', '{succeeded} actions executed')\n .replace('{succeeded}', String(result.result.succeeded))\n + (result.result.failed > 0 ? `, ${result.result.failed} failed` : ''), 'success')\n await loadData()\n } else {\n flash(t('inbox_ops.flash.accept_all_failed', 'Failed to accept all actions'), 'error')\n }\n setIsProcessing(false)\n }, [proposalId, actions, confirm, t, loadData])\n\n const handleRejectAll = React.useCallback(async () => {\n const confirmed = await confirm({\n title: t('inbox_ops.action.reject_all', 'Reject Proposal'),\n text: t('inbox_ops.action.reject_all_confirm', 'Reject all pending actions in this proposal?'),\n })\n if (!confirmed) return\n\n setIsProcessing(true)\n const result = await apiCall<{ ok: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/reject`,\n { method: 'POST' },\n )\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.action.proposal_rejected', 'Proposal rejected'), 'success')\n await loadData()\n }\n setIsProcessing(false)\n }, [proposalId, confirm, t, loadData])\n\n const handleRetryExtraction = React.useCallback(async () => {\n if (!email) return\n setIsProcessing(true)\n const result = await apiCall<{ ok: boolean }>(\n `/api/inbox_ops/emails/${email.id}/reprocess`,\n { method: 'POST' },\n )\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.flash.reprocessing_started', 'Reprocessing started'), 'success')\n await loadData()\n }\n setIsProcessing(false)\n }, [email, loadData])\n\n const handleSendReply = React.useCallback(async (actionId: string) => {\n setSendingReplyId(actionId)\n const result = await apiCall<{ ok: boolean; error?: string }>(\n `/api/inbox_ops/proposals/${proposalId}/replies/${actionId}/send`,\n { method: 'POST' },\n )\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.reply.sent_success', 'Reply sent successfully'), 'success')\n await loadData()\n } else {\n flash(result?.result?.error || t('inbox_ops.flash.send_reply_failed', 'Failed to send reply'), 'error')\n }\n setSendingReplyId(null)\n }, [proposalId, t, loadData])\n\n if (isLoading) return <LoadingMessage label={t('inbox_ops.loading_proposal', 'Loading proposal...')} />\n\n const pendingActions = actions.filter((a) => a.status === 'pending')\n const emailIsProcessing = email?.status === 'processing'\n const emailFailed = email?.status === 'failed'\n\n return (\n <Page>\n {ConfirmDialogElement}\n {editingAction && (\n <EditActionDialog\n action={editingAction}\n actionTypeLabels={actionTypeLabels}\n onClose={() => setEditingAction(null)}\n onSaved={loadData}\n />\n )}\n\n <div className=\"flex items-center justify-between px-3 py-3 md:px-6 md:py-4 border-b\">\n <div className=\"flex items-center gap-3\">\n <Link href=\"/backend/inbox-ops\">\n <Button variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n </Link>\n <div className=\"min-w-0\">\n <h1 className=\"text-lg font-semibold truncate\">{email?.subject || t('inbox_ops.proposal', 'Proposal')}</h1>\n <p className=\"text-xs text-muted-foreground\">\n {email?.forwardedByName || email?.forwardedByAddress} \u00B7 {email?.receivedAt && new Date(email.receivedAt).toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {pendingActions.length > 0 && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-11 md:h-9 text-destructive border-destructive/30 hover:bg-destructive/10\"\n onClick={handleRejectAll}\n disabled={isProcessing}\n >\n <XCircle className=\"h-4 w-4 mr-1\" />\n <span className=\"hidden md:inline\">{t('inbox_ops.action.reject_all', 'Reject Proposal')}</span>\n </Button>\n )}\n {pendingActions.length > 1 && (\n <Button size=\"sm\" className=\"h-11 md:h-9\" onClick={handleAcceptAll} disabled={isProcessing}>\n {isProcessing ? <Loader2 className=\"h-4 w-4 animate-spin mr-1\" /> : <CheckCheck className=\"h-4 w-4 mr-1\" />}\n <span className=\"hidden md:inline\">{t('inbox_ops.action.accept_all', 'Accept All')}</span>\n </Button>\n )}\n </div>\n </div>\n\n <PageBody>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6\">\n {/* Left panel: Email Thread */}\n <div>\n <EmailThreadViewer email={email} />\n </div>\n\n {/* Right panel: Summary + Actions */}\n <div className=\"space-y-4\">\n {emailIsProcessing ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary mb-3\" />\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.extraction_loading', 'AI is analyzing this thread...')}</p>\n </div>\n ) : emailFailed ? (\n <div className=\"border rounded-lg p-4 bg-red-50 dark:bg-red-950/20\">\n <div className=\"flex items-center gap-2 mb-2\">\n <AlertTriangle className=\"h-5 w-5 text-red-600\" />\n <span className=\"text-sm font-medium text-red-700\">{t('inbox_ops.extraction_failed', 'Extraction failed')}</span>\n </div>\n {email?.processingError && (\n <p className=\"text-xs text-red-600 mb-3\">{email.processingError}</p>\n )}\n <Button size=\"sm\" variant=\"outline\" onClick={handleRetryExtraction} disabled={isProcessing}>\n <RefreshCw className=\"h-4 w-4 mr-1\" />\n {t('inbox_ops.action.retry', 'Retry')}\n </Button>\n </div>\n ) : proposal ? (\n <>\n {/* Summary */}\n <div className=\"border rounded-lg p-3 md:p-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"font-semibold text-sm\">{t('inbox_ops.summary', 'Summary')}</h3>\n {proposal.workingLanguage && proposal.workingLanguage !== locale && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs\"\n onClick={showTranslation ? () => setShowTranslation(false) : handleTranslate}\n disabled={isTranslating}\n >\n {isTranslating ? (\n <Loader2 className=\"h-3 w-3 animate-spin mr-1\" />\n ) : (\n <Languages className=\"h-3 w-3 mr-1\" />\n )}\n {showTranslation\n ? t('inbox_ops.translate.show_original', 'Show original')\n : t('inbox_ops.translate.translate', 'Translate')}\n </Button>\n )}\n </div>\n <p className=\"text-sm text-foreground/80 mb-3\">\n {showTranslation && translation ? translation.summary : proposal.summary}\n </p>\n\n <div className=\"flex items-center gap-4 mb-3\">\n <div>\n <span className=\"text-xs text-muted-foreground\">{t('inbox_ops.confidence', 'Confidence')}</span>\n <ConfidenceBadge value={proposal.confidence} />\n </div>\n </div>\n\n {proposal.possiblyIncomplete && (\n <div className=\"flex items-center gap-2 text-xs text-yellow-600 bg-yellow-50 dark:bg-yellow-950/20 rounded px-2 py-1 mb-3\">\n <AlertTriangle className=\"h-3 w-3\" />\n {t('inbox_ops.possibly_incomplete', 'This thread appears to be a partial forward')}\n </div>\n )}\n\n {/* Participants */}\n {proposal.participants.length > 0 && (\n <div>\n <h4 className=\"text-xs font-medium text-muted-foreground mb-1\">{t('inbox_ops.participants', 'Participants')}</h4>\n <div className=\"space-y-1\">\n {proposal.participants.map((p, idx) => (\n <div key={idx} className=\"flex items-center gap-2 text-sm\">\n <Users className=\"h-3 w-3 text-muted-foreground\" />\n <span>{p.name}</span>\n <span className=\"text-xs text-muted-foreground\">({p.role})</span>\n {p.matchedContactId && <CheckCircle className=\"h-3 w-3 text-green-500\" />}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Discrepancies not tied to a specific action */}\n {(() => {\n const actionIds = new Set(actions.map((a) => a.id))\n const general = discrepancies.filter((d) => !d.resolved && (!d.actionId || !actionIds.has(d.actionId)))\n if (general.length === 0) return null\n return (\n <div className=\"border rounded-lg p-3 md:p-4 bg-yellow-50 dark:bg-yellow-950/20\">\n <div className=\"flex items-center gap-2 mb-2\">\n <AlertTriangle className=\"h-4 w-4 text-yellow-600\" />\n <h3 className=\"font-semibold text-sm text-yellow-800 dark:text-yellow-300\">{t('inbox_ops.discrepancies', 'Issues Detected')}</h3>\n </div>\n <div className=\"space-y-1.5\">\n {general.map((d) => (\n <div key={d.id} className={`flex items-start gap-2 text-xs rounded px-2 py-1.5 ${\n d.severity === 'error' ? 'bg-red-100 text-red-700 dark:bg-red-950/30' : 'bg-yellow-100 text-yellow-700 dark:bg-yellow-950/30'\n }`}>\n <AlertTriangle className=\"h-3 w-3 mt-0.5 flex-shrink-0\" />\n <div>\n <span>{d.description}</span>\n {(d.expectedValue || d.foundValue) && (\n <div className=\"mt-0.5 text-[11px] opacity-80\">\n {d.expectedValue && <span>Expected: {d.expectedValue}</span>}\n {d.expectedValue && d.foundValue && <span> \u00B7 </span>}\n {d.foundValue && <span>Found: {d.foundValue}</span>}\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n })()}\n\n {/* Actions */}\n <div>\n <h3 className=\"font-semibold text-sm mb-2\">{t('inbox_ops.actions', 'Proposed Actions')}</h3>\n {actions.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.no_actions', 'No actionable items detected in this thread')}</p>\n ) : (\n <div className=\"space-y-3\">\n {actions.map((action) => (\n <div key={action.id}>\n <ActionCard\n action={action}\n discrepancies={discrepancies}\n actionTypeLabels={actionTypeLabels}\n onAccept={handleAcceptAction}\n onReject={handleRejectAction}\n onRetry={handleAcceptAction}\n onEdit={handleEditAction}\n translatedDescription={showTranslation ? translation?.actions[action.id] : undefined}\n />\n {action.actionType === 'draft_reply' && (action.status === 'executed' || action.status === 'accepted') && (\n <div className=\"mt-2 pl-7\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n className=\"h-11 md:h-9\"\n disabled={sendingReplyId === action.id}\n onClick={() => handleSendReply(action.id)}\n >\n {sendingReplyId === action.id ? (\n <Loader2 className=\"h-4 w-4 animate-spin mr-1\" />\n ) : (\n <ExternalLink className=\"h-4 w-4 mr-1\" />\n )}\n {sendingReplyId === action.id\n ? t('inbox_ops.reply.sending', 'Sending...')\n : t('inbox_ops.reply.send', 'Send Reply')}\n </Button>\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n </>\n ) : null}\n </div>\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { useT, useLocale } 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 ArrowLeft,\n CheckCircle,\n XCircle,\n AlertTriangle,\n CheckCheck,\n Loader2,\n ExternalLink,\n RefreshCw,\n Users,\n Languages,\n} from 'lucide-react'\nimport type { ProposalTranslationEntry } from '../../../../data/entities'\nimport type { ProposalDetail, ActionDetail, DiscrepancyDetail, EmailDetail } from '../../../../components/proposals/types'\nimport { ActionCard, ConfidenceBadge, useActionTypeLabels, useDiscrepancyDescriptions } from '../../../../components/proposals/ActionCard'\nimport { hasContactNameIssue } from '../../../../lib/contactValidation'\nimport { EditActionDialog } from '../../../../components/proposals/EditActionDialog'\n\nfunction EmailThreadViewer({ email }: { email: EmailDetail | null }) {\n const t = useT()\n if (!email) return null\n\n const messages = email.threadMessages || []\n\n return (\n <div className=\"space-y-3\">\n <h3 className=\"font-semibold text-sm\">{t('inbox_ops.email_thread', 'Email Thread')}</h3>\n {messages.length > 0 ? (\n messages.map((msg, index) => (\n <div key={index} className=\"border rounded-lg p-3 md:p-4 bg-card\">\n <div className=\"flex items-center gap-2 mb-2\">\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium truncate\">\n {msg.from?.name || msg.from?.email || t('inbox_ops.sender_unknown', 'Unknown')}\n </div>\n <div className=\"text-xs text-muted-foreground truncate\">{msg.from?.email}</div>\n </div>\n <span className=\"text-xs text-muted-foreground whitespace-nowrap\">\n {msg.date ? new Date(msg.date).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : ''}\n </span>\n </div>\n <div className=\"text-sm whitespace-pre-wrap text-foreground/80\">{msg.body}</div>\n </div>\n ))\n ) : email.cleanedText ? (\n <div className=\"border rounded-lg p-3 md:p-4 bg-card\">\n <div className=\"text-sm whitespace-pre-wrap text-foreground/80\">{email.cleanedText}</div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.no_email_content', 'No email content available')}</p>\n )}\n </div>\n )\n}\n\nexport default function ProposalDetailPage({ params }: { params?: { id?: string } }) {\n const t = useT()\n const locale = useLocale()\n const router = useRouter()\n const proposalId = params?.id\n\n const [proposal, setProposal] = React.useState<ProposalDetail | null>(null)\n const [actions, setActions] = React.useState<ActionDetail[]>([])\n const [discrepancies, setDiscrepancies] = React.useState<DiscrepancyDetail[]>([])\n const [email, setEmail] = React.useState<EmailDetail | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isProcessing, setIsProcessing] = React.useState(false)\n\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'inbox-ops-proposal-detail',\n })\n const actionTypeLabels = useActionTypeLabels()\n const resolveDiscrepancyDescription = useDiscrepancyDescriptions()\n const [editingAction, setEditingAction] = React.useState<ActionDetail | null>(null)\n const [sendingReplyId, setSendingReplyId] = React.useState<string | null>(null)\n\n const [translation, setTranslation] = React.useState<ProposalTranslationEntry | null>(null)\n const [isTranslating, setIsTranslating] = React.useState(false)\n const [showTranslation, setShowTranslation] = React.useState(false)\n\n const handleEditAction = React.useCallback((action: ActionDetail) => {\n if (action.actionType === 'create_order' || action.actionType === 'create_quote') {\n const kind = action.actionType === 'create_order' ? 'order' : 'quote'\n try {\n sessionStorage.setItem(\n 'inbox_ops.orderDraft',\n JSON.stringify({\n actionId: action.id,\n proposalId: action.proposalId,\n payload: action.payload,\n }),\n )\n } catch { /* sessionStorage unavailable */ }\n router.push(`/backend/sales/documents/create?kind=${kind}&fromInboxAction=${encodeURIComponent(action.id)}`)\n return\n }\n if (action.actionType === 'create_product') {\n try {\n sessionStorage.setItem(\n 'inbox_ops.productDraft',\n JSON.stringify({\n actionId: action.id,\n proposalId: action.proposalId,\n payload: action.payload,\n }),\n )\n } catch { /* sessionStorage unavailable */ }\n router.push(`/backend/catalog/products/create?fromInboxAction=${encodeURIComponent(action.id)}`)\n return\n }\n setEditingAction(action)\n }, [router])\n\n const handleTranslate = React.useCallback(async () => {\n if (!proposalId) return\n setIsTranslating(true)\n const result = await runMutation({\n operation: () => apiCall<{ translation: ProposalTranslationEntry; cached: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/translate`,\n { method: 'POST', body: JSON.stringify({ targetLocale: locale }) },\n ),\n context: {},\n })\n if (result?.ok && result.result?.translation) {\n setTranslation(result.result.translation)\n setShowTranslation(true)\n } else {\n const detail = (result?.result as Record<string, unknown> | null)?.error\n flash(detail ? `${t('inbox_ops.translate.failed', 'Translation failed')}: ${detail}` : t('inbox_ops.translate.failed', 'Translation failed'), 'error')\n }\n setIsTranslating(false)\n }, [proposalId, locale, t, runMutation])\n\n const loadData = React.useCallback(async () => {\n if (!proposalId) return\n setIsLoading(true)\n setError(null)\n try {\n const result = await apiCall<{\n proposal: ProposalDetail\n actions: ActionDetail[]\n discrepancies: DiscrepancyDetail[]\n email: EmailDetail\n }>(`/api/inbox_ops/proposals/${proposalId}`)\n if (result?.ok && result.result) {\n setProposal(result.result.proposal)\n setActions(result.result.actions || [])\n setDiscrepancies(result.result.discrepancies || [])\n setEmail(result.result.email)\n } else {\n setError(t('inbox_ops.flash.load_failed', 'Failed to load proposal'))\n }\n } catch {\n setError(t('inbox_ops.flash.load_failed', 'Failed to load proposal'))\n }\n setIsLoading(false)\n }, [proposalId, t])\n\n React.useEffect(() => { loadData() }, [loadData])\n\n const handleAcceptAction = React.useCallback(async (actionId: string) => {\n setIsProcessing(true)\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean; error?: string }>(\n `/api/inbox_ops/proposals/${proposalId}/actions/${actionId}/accept`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.flash.action_executed', 'Action executed'), 'success')\n await loadData()\n } else {\n flash(result?.result?.error || t('inbox_ops.flash.action_execute_failed', 'Failed to execute action'), 'error')\n }\n setIsProcessing(false)\n }, [proposalId, loadData, t, runMutation])\n\n const handleRejectAction = React.useCallback(async (actionId: string) => {\n setIsProcessing(true)\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/actions/${actionId}/reject`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.flash.action_rejected', 'Action rejected'), 'success')\n await loadData()\n } else {\n flash(t('inbox_ops.flash.action_reject_failed', 'Failed to reject action'), 'error')\n }\n setIsProcessing(false)\n }, [proposalId, loadData, runMutation])\n\n const handleAcceptAll = React.useCallback(async () => {\n const pendingActions = actions.filter((a) => a.status === 'pending')\n const pendingCount = pendingActions.length\n const nameIssueCount = pendingActions.filter((a) => hasContactNameIssue(a)).length\n\n const confirmText = nameIssueCount > 0\n ? t('inbox_ops.action.accept_all_confirm_with_skip', 'Execute {count} pending actions? {skipCount} contact actions will be skipped due to missing names.')\n .replace('{count}', String(pendingCount))\n .replace('{skipCount}', String(nameIssueCount))\n : t('inbox_ops.action.accept_all_confirm', 'Execute {count} pending actions?').replace('{count}', String(pendingCount))\n\n const confirmed = await confirm({\n title: t('inbox_ops.action.accept_all', 'Accept All'),\n text: confirmText,\n })\n if (!confirmed) return\n\n setIsProcessing(true)\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean; succeeded: number; failed: number }>(\n `/api/inbox_ops/proposals/${proposalId}/accept-all`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n const msg = result.result.failed > 0\n ? t('inbox_ops.flash.accept_all_partial', '{succeeded} actions executed, {failed} failed')\n .replace('{succeeded}', String(result.result.succeeded))\n .replace('{failed}', String(result.result.failed))\n : t('inbox_ops.flash.accept_all_success', '{succeeded} actions executed')\n .replace('{succeeded}', String(result.result.succeeded))\n flash(msg, 'success')\n await loadData()\n } else {\n flash(t('inbox_ops.flash.accept_all_failed', 'Failed to accept all actions'), 'error')\n }\n setIsProcessing(false)\n }, [proposalId, actions, confirm, t, loadData, runMutation])\n\n const handleRejectAll = React.useCallback(async () => {\n const confirmed = await confirm({\n title: t('inbox_ops.action.reject_all', 'Reject Proposal'),\n text: t('inbox_ops.action.reject_all_confirm', 'Reject all pending actions in this proposal?'),\n })\n if (!confirmed) return\n\n setIsProcessing(true)\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean }>(\n `/api/inbox_ops/proposals/${proposalId}/reject`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.action.proposal_rejected', 'Proposal rejected'), 'success')\n await loadData()\n }\n setIsProcessing(false)\n }, [proposalId, confirm, t, loadData, runMutation])\n\n const handleRetryExtraction = React.useCallback(async () => {\n if (!email) return\n setIsProcessing(true)\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean }>(\n `/api/inbox_ops/emails/${email.id}/reprocess`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.flash.reprocessing_started', 'Reprocessing started'), 'success')\n await loadData()\n }\n setIsProcessing(false)\n }, [email, loadData, runMutation])\n\n const handleSendReply = React.useCallback(async (actionId: string) => {\n setSendingReplyId(actionId)\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean; error?: string }>(\n `/api/inbox_ops/proposals/${proposalId}/replies/${actionId}/send`,\n { method: 'POST' },\n ),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n flash(t('inbox_ops.reply.sent_success', 'Reply sent successfully'), 'success')\n await loadData()\n } else {\n flash(result?.result?.error || t('inbox_ops.flash.send_reply_failed', 'Failed to send reply'), 'error')\n }\n setSendingReplyId(null)\n }, [proposalId, t, loadData, runMutation])\n\n if (isLoading) return <LoadingMessage label={t('inbox_ops.loading_proposal', 'Loading proposal...')} />\n if (error) return <ErrorMessage label={error} />\n\n const pendingActions = actions.filter((a) => a.status === 'pending')\n const emailIsProcessing = email?.status === 'processing'\n const emailFailed = email?.status === 'failed'\n\n return (\n <Page>\n {ConfirmDialogElement}\n {editingAction && (\n <EditActionDialog\n action={editingAction}\n actionTypeLabels={actionTypeLabels}\n onClose={() => setEditingAction(null)}\n onSaved={loadData}\n />\n )}\n\n <div className=\"flex items-center justify-between px-4 py-3 md:px-6 md:py-4 border-b bg-background\">\n <div className=\"flex items-center gap-2 md:gap-3 min-w-0\">\n <Link href=\"/backend/inbox-ops\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n </Link>\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-base md:text-lg font-semibold truncate\">{email?.subject || t('inbox_ops.proposal', 'Proposal')}</h1>\n <p className=\"text-xs text-muted-foreground truncate\">\n {email?.forwardedByName || email?.forwardedByAddress} \u00B7 {email?.receivedAt && new Date(email.receivedAt).toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {pendingActions.length > 0 && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"h-11 md:h-9 text-destructive border-destructive/30 hover:bg-destructive/10\"\n onClick={handleRejectAll}\n disabled={isProcessing}\n >\n <XCircle className=\"h-4 w-4 mr-1\" />\n <span className=\"hidden md:inline\">{t('inbox_ops.action.reject_all', 'Reject Proposal')}</span>\n </Button>\n )}\n {pendingActions.length > 1 && (\n <Button type=\"button\" size=\"sm\" className=\"h-11 md:h-9\" onClick={handleAcceptAll} disabled={isProcessing}>\n {isProcessing ? <Loader2 className=\"h-4 w-4 animate-spin mr-1\" /> : <CheckCheck className=\"h-4 w-4 mr-1\" />}\n <span className=\"hidden md:inline\">{t('inbox_ops.action.accept_all', 'Accept All')}</span>\n </Button>\n )}\n </div>\n </div>\n\n <PageBody>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-6\">\n {/* Left panel: Email Thread */}\n <div>\n <EmailThreadViewer email={email} />\n </div>\n\n {/* Right panel: Summary + Actions */}\n <div className=\"space-y-4\">\n {emailIsProcessing ? (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary mb-3\" />\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.extraction_loading', 'AI is analyzing this thread...')}</p>\n </div>\n ) : emailFailed ? (\n <div className=\"border rounded-lg p-4 bg-red-50 dark:bg-red-950/20\">\n <div className=\"flex items-center gap-2 mb-2\">\n <AlertTriangle className=\"h-5 w-5 text-red-600\" />\n <span className=\"text-sm font-medium text-red-700\">{t('inbox_ops.extraction_failed', 'Extraction failed')}</span>\n </div>\n {email?.processingError && (\n <p className=\"text-xs text-red-600 mb-3\">{email.processingError}</p>\n )}\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={handleRetryExtraction} disabled={isProcessing}>\n <RefreshCw className=\"h-4 w-4 mr-1\" />\n {t('inbox_ops.action.retry', 'Retry')}\n </Button>\n </div>\n ) : proposal ? (\n <>\n {/* Summary */}\n <div className=\"border rounded-lg p-3 md:p-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"font-semibold text-sm\">{t('inbox_ops.summary', 'Summary')}</h3>\n {(proposal.workingLanguage || 'en') !== locale && (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs\"\n onClick={showTranslation ? () => setShowTranslation(false) : handleTranslate}\n disabled={isTranslating}\n >\n {isTranslating ? (\n <Loader2 className=\"h-3 w-3 animate-spin mr-1\" />\n ) : (\n <Languages className=\"h-3 w-3 mr-1\" />\n )}\n {showTranslation\n ? t('inbox_ops.translate.show_original', 'Show original')\n : t('inbox_ops.translate.translate', 'Translate')}\n </Button>\n )}\n </div>\n <p className=\"text-sm text-foreground/80 mb-3\">\n {showTranslation && translation ? translation.summary : proposal.summary}\n </p>\n\n <div className=\"flex items-center gap-4 mb-3\">\n <div>\n <span className=\"text-xs text-muted-foreground\">{t('inbox_ops.confidence', 'Confidence')}</span>\n <ConfidenceBadge value={proposal.confidence} />\n </div>\n </div>\n\n {proposal.possiblyIncomplete && (\n <div className=\"flex items-center gap-2 text-xs text-yellow-600 bg-yellow-50 dark:bg-yellow-950/20 rounded px-2 py-1 mb-3\">\n <AlertTriangle className=\"h-3 w-3\" />\n {t('inbox_ops.possibly_incomplete', 'This thread appears to be a partial forward')}\n </div>\n )}\n\n {/* Participants */}\n {proposal.participants.length > 0 && (\n <div>\n <h4 className=\"text-xs font-medium text-muted-foreground mb-1\">{t('inbox_ops.participants', 'Participants')}</h4>\n <div className=\"space-y-1\">\n {proposal.participants.map((p, idx) => (\n <div key={idx} className=\"flex items-center gap-2 text-sm\">\n <Users className=\"h-3 w-3 text-muted-foreground\" />\n <span>{p.name}</span>\n <span className=\"text-xs text-muted-foreground\">({p.role})</span>\n {p.matchedContactId && <CheckCircle className=\"h-3 w-3 text-green-500\" />}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Discrepancies not tied to a specific action */}\n {(() => {\n const actionIds = new Set(actions.map((a) => a.id))\n const general = discrepancies.filter((d) => !d.resolved && (!d.actionId || !actionIds.has(d.actionId)))\n if (general.length === 0) return null\n return (\n <div className=\"border rounded-lg p-3 md:p-4 bg-yellow-50 dark:bg-yellow-950/20\">\n <div className=\"flex items-center gap-2 mb-2\">\n <AlertTriangle className=\"h-4 w-4 text-yellow-600\" />\n <h3 className=\"font-semibold text-sm text-yellow-800 dark:text-yellow-300\">{t('inbox_ops.discrepancies', 'Issues Detected')}</h3>\n </div>\n <div className=\"space-y-1.5\">\n {general.map((d) => (\n <div key={d.id} className={`flex items-start gap-2 text-xs rounded px-2 py-1.5 ${\n d.severity === 'error' ? 'bg-red-100 text-red-700 dark:bg-red-950/30' : 'bg-yellow-100 text-yellow-700 dark:bg-yellow-950/30'\n }`}>\n <AlertTriangle className=\"h-3 w-3 mt-0.5 flex-shrink-0\" />\n <div>\n <span>{resolveDiscrepancyDescription(d.description, d.foundValue)}</span>\n {(d.expectedValue || d.foundValue) && (\n <div className=\"mt-0.5 text-[11px] opacity-80\">\n {d.expectedValue && <span>{t('inbox_ops.discrepancy.expected', 'Expected')}: {d.expectedValue}</span>}\n {d.expectedValue && d.foundValue && <span> \u00B7 </span>}\n {d.foundValue && <span>{t('inbox_ops.discrepancy.found', 'Found')}: {d.foundValue}</span>}\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n )\n })()}\n\n {/* Actions */}\n <div>\n <h3 className=\"font-semibold text-sm mb-2\">{t('inbox_ops.actions', 'Proposed Actions')}</h3>\n {actions.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.no_actions', 'No actionable items detected in this thread')}</p>\n ) : (\n <div className=\"space-y-3\">\n {actions.map((action) => (\n <div key={action.id}>\n <ActionCard\n action={action}\n discrepancies={discrepancies}\n actionTypeLabels={actionTypeLabels}\n onAccept={handleAcceptAction}\n onReject={handleRejectAction}\n onRetry={handleAcceptAction}\n onEdit={handleEditAction}\n translatedDescription={showTranslation ? translation?.actions[action.id] : undefined}\n resolveDiscrepancyDescription={resolveDiscrepancyDescription}\n />\n {action.actionType === 'draft_reply' && (action.status === 'executed' || action.status === 'accepted') && (\n <div className=\"mt-2 pl-7\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n className=\"h-11 md:h-9\"\n disabled={sendingReplyId === action.id}\n onClick={() => handleSendReply(action.id)}\n >\n {sendingReplyId === action.id ? (\n <Loader2 className=\"h-4 w-4 animate-spin mr-1\" />\n ) : (\n <ExternalLink className=\"h-4 w-4 mr-1\" />\n )}\n {sendingReplyId === action.id\n ? t('inbox_ops.reply.sending', 'Sending...')\n : t('inbox_ops.reply.send', 'Send Reply')}\n </Button>\n </div>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n </>\n ) : null}\n </div>\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["pendingActions"]
7
7
  }
@@ -3,11 +3,11 @@ const metadata = {
3
3
  requireFeatures: ["inbox_ops.proposals.view"],
4
4
  pageTitle: "Proposal",
5
5
  pageTitleKey: "inbox_ops.nav.proposal_detail",
6
- pageGroup: "InboxOps",
6
+ pageGroup: "AI Inbox Actions",
7
7
  pageGroupKey: "inbox_ops.nav.group",
8
8
  navHidden: true,
9
9
  breadcrumb: [
10
- { label: "InboxOps", labelKey: "inbox_ops.nav.group", href: "/backend/inbox-ops" },
10
+ { label: "AI Inbox Actions", labelKey: "inbox_ops.nav.group", href: "/backend/inbox-ops" },
11
11
  { label: "Proposal", labelKey: "inbox_ops.nav.proposal_detail" }
12
12
  ]
13
13
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/inbox_ops/backend/inbox-ops/proposals/%5Bid%5D/page.meta.ts"],
4
- "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['inbox_ops.proposals.view'],\n pageTitle: 'Proposal',\n pageTitleKey: 'inbox_ops.nav.proposal_detail',\n pageGroup: 'InboxOps',\n pageGroupKey: 'inbox_ops.nav.group',\n navHidden: true,\n breadcrumb: [\n { label: 'InboxOps', labelKey: 'inbox_ops.nav.group', href: '/backend/inbox-ops' },\n { label: 'Proposal', labelKey: 'inbox_ops.nav.proposal_detail' },\n ],\n}\n"],
5
- "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,0BAA0B;AAAA,EAC5C,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,OAAO,YAAY,UAAU,uBAAuB,MAAM,qBAAqB;AAAA,IACjF,EAAE,OAAO,YAAY,UAAU,gCAAgC;AAAA,EACjE;AACF;",
4
+ "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['inbox_ops.proposals.view'],\n pageTitle: 'Proposal',\n pageTitleKey: 'inbox_ops.nav.proposal_detail',\n pageGroup: 'AI Inbox Actions',\n pageGroupKey: 'inbox_ops.nav.group',\n navHidden: true,\n breadcrumb: [\n { label: 'AI Inbox Actions', labelKey: 'inbox_ops.nav.group', href: '/backend/inbox-ops' },\n { label: 'Proposal', labelKey: 'inbox_ops.nav.proposal_detail' },\n ],\n}\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,0BAA0B;AAAA,EAC5C,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,OAAO,oBAAoB,UAAU,uBAAuB,MAAM,qBAAqB;AAAA,IACzF,EAAE,OAAO,YAAY,UAAU,gCAAgC;AAAA,EACjE;AACF;",
6
6
  "names": []
7
7
  }
@@ -6,28 +6,50 @@ import { Page, PageBody } from "@open-mercato/ui/backend/Page";
6
6
  import { Button } from "@open-mercato/ui/primitives/button";
7
7
  import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
8
8
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
9
+ import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
9
10
  import { useT } from "@open-mercato/shared/lib/i18n/context";
11
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
10
12
  import { ArrowLeft, Copy, CheckCircle } from "lucide-react";
11
13
  const LANGUAGE_KEYS = ["en", "de", "es", "pl"];
12
14
  function InboxSettingsPage() {
13
15
  const t = useT();
16
+ const { runMutation } = useGuardedMutation({
17
+ contextId: "inbox-ops-settings"
18
+ });
14
19
  const languageOptions = LANGUAGE_KEYS.map((key) => ({
15
20
  value: key,
16
21
  label: t(`inbox_ops.settings.language_${key}`, key)
17
22
  }));
18
23
  const [settings, setSettings] = React.useState(null);
19
24
  const [isLoading, setIsLoading] = React.useState(true);
25
+ const [error, setError] = React.useState(null);
20
26
  const [copied, setCopied] = React.useState(false);
21
27
  const [isSavingLanguage, setIsSavingLanguage] = React.useState(false);
22
28
  React.useEffect(() => {
29
+ let cancelled = false;
23
30
  async function load() {
24
31
  setIsLoading(true);
25
- const result = await apiCall("/api/inbox_ops/settings");
26
- if (result?.ok && result.result?.settings) setSettings(result.result.settings);
27
- setIsLoading(false);
32
+ setError(null);
33
+ try {
34
+ const result = await apiCall("/api/inbox_ops/settings");
35
+ if (!cancelled) {
36
+ if (result?.ok && result.result?.settings) {
37
+ setSettings(result.result.settings);
38
+ } else {
39
+ setError(t("inbox_ops.settings.load_failed", "Failed to load settings"));
40
+ }
41
+ }
42
+ } catch {
43
+ if (!cancelled) setError(t("inbox_ops.settings.load_failed", "Failed to load settings"));
44
+ } finally {
45
+ if (!cancelled) setIsLoading(false);
46
+ }
28
47
  }
29
48
  load();
30
- }, []);
49
+ return () => {
50
+ cancelled = true;
51
+ };
52
+ }, [t]);
31
53
  const handleCopy = React.useCallback(() => {
32
54
  if (settings?.inboxAddress) {
33
55
  navigator.clipboard.writeText(settings.inboxAddress);
@@ -38,9 +60,12 @@ function InboxSettingsPage() {
38
60
  const handleLanguageChange = React.useCallback(async (event) => {
39
61
  const workingLanguage = event.target.value;
40
62
  setIsSavingLanguage(true);
41
- const result = await apiCall("/api/inbox_ops/settings", {
42
- method: "PATCH",
43
- body: JSON.stringify({ workingLanguage })
63
+ const result = await runMutation({
64
+ operation: () => apiCall("/api/inbox_ops/settings", {
65
+ method: "PATCH",
66
+ body: JSON.stringify({ workingLanguage })
67
+ }),
68
+ context: {}
44
69
  });
45
70
  if (result?.ok && result.result?.ok) {
46
71
  setSettings((prev) => prev ? { ...prev, workingLanguage: result.result.settings.workingLanguage } : prev);
@@ -49,22 +74,19 @@ function InboxSettingsPage() {
49
74
  flash(t("inbox_ops.settings.language_save_failed", "Failed to update working language"), "error");
50
75
  }
51
76
  setIsSavingLanguage(false);
52
- }, [t]);
77
+ }, [t, runMutation]);
53
78
  return /* @__PURE__ */ jsxs(Page, { children: [
54
79
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-3 py-3 md:px-6 md:py-4", children: [
55
- /* @__PURE__ */ jsx(Link, { href: "/backend/inbox-ops", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }) }) }),
80
+ /* @__PURE__ */ jsx(Link, { href: "/backend/inbox-ops", children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4" }) }) }),
56
81
  /* @__PURE__ */ jsx("h1", { className: "text-lg font-semibold", children: t("inbox_ops.settings.title", "Inbox Settings") })
57
82
  ] }),
58
- /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx("div", { className: "max-w-lg", children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "animate-pulse space-y-4", children: [
59
- /* @__PURE__ */ jsx("div", { className: "h-6 bg-muted rounded w-1/3" }),
60
- /* @__PURE__ */ jsx("div", { className: "h-12 bg-muted rounded" })
61
- ] }) : settings ? /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
83
+ /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx("div", { className: "max-w-lg", children: isLoading ? /* @__PURE__ */ jsx(LoadingMessage, { label: t("inbox_ops.settings.loading", "Loading settings...") }) : error ? /* @__PURE__ */ jsx(ErrorMessage, { label: error }) : settings ? /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
62
84
  /* @__PURE__ */ jsxs("div", { children: [
63
85
  /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: t("inbox_ops.settings.forwarding_address", "Forwarding Address") }),
64
86
  /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t("inbox_ops.settings.forwarding_hint", "Forward email threads to this address to create proposals") }),
65
87
  /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
66
88
  /* @__PURE__ */ jsx("div", { className: "flex-1 bg-muted rounded-lg px-4 py-3", children: /* @__PURE__ */ jsx("code", { className: "text-sm font-mono", children: settings.inboxAddress }) }),
67
- /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "h-11 md:h-9", onClick: handleCopy, children: [
89
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-11 md:h-9", onClick: handleCopy, children: [
68
90
  copied ? /* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4 text-green-600" }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" }),
69
91
  /* @__PURE__ */ jsx("span", { className: "ml-1", children: copied ? t("inbox_ops.settings.copied", "Copied") : t("inbox_ops.settings.copy", "Copy") })
70
92
  ] })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { ArrowLeft, Copy, CheckCircle } from 'lucide-react'\n\nconst LANGUAGE_KEYS = ['en', 'de', 'es', 'pl'] as const\n\nexport default function InboxSettingsPage() {\n const t = useT()\n\n const languageOptions = LANGUAGE_KEYS.map((key) => ({\n value: key,\n label: t(`inbox_ops.settings.language_${key}` as never, key),\n }))\n const [settings, setSettings] = React.useState<{ inboxAddress?: string; isActive?: boolean; workingLanguage?: string } | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [copied, setCopied] = React.useState(false)\n const [isSavingLanguage, setIsSavingLanguage] = React.useState(false)\n\n React.useEffect(() => {\n async function load() {\n setIsLoading(true)\n const result = await apiCall<{ settings: { inboxAddress?: string; isActive?: boolean; workingLanguage?: string } | null }>('/api/inbox_ops/settings')\n if (result?.ok && result.result?.settings) setSettings(result.result.settings)\n setIsLoading(false)\n }\n load()\n }, [])\n\n const handleCopy = React.useCallback(() => {\n if (settings?.inboxAddress) {\n navigator.clipboard.writeText(settings.inboxAddress)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }, [settings])\n\n const handleLanguageChange = React.useCallback(async (event: React.ChangeEvent<HTMLSelectElement>) => {\n const workingLanguage = event.target.value\n setIsSavingLanguage(true)\n const result = await apiCall<{ ok: boolean; settings: { workingLanguage: string } }>('/api/inbox_ops/settings', {\n method: 'PATCH',\n body: JSON.stringify({ workingLanguage }),\n })\n if (result?.ok && result.result?.ok) {\n setSettings((prev) => prev ? { ...prev, workingLanguage: result.result!.settings.workingLanguage } : prev)\n flash(t('inbox_ops.settings.language_saved', 'Working language updated'), 'success')\n } else {\n flash(t('inbox_ops.settings.language_save_failed', 'Failed to update working language'), 'error')\n }\n setIsSavingLanguage(false)\n }, [t])\n\n return (\n <Page>\n <div className=\"flex items-center gap-3 px-3 py-3 md:px-6 md:py-4\">\n <Link href=\"/backend/inbox-ops\">\n <Button variant=\"ghost\" size=\"sm\"><ArrowLeft className=\"h-4 w-4\" /></Button>\n </Link>\n <h1 className=\"text-lg font-semibold\">{t('inbox_ops.settings.title', 'Inbox Settings')}</h1>\n </div>\n\n <PageBody>\n <div className=\"max-w-lg\">\n {isLoading ? (\n <div className=\"animate-pulse space-y-4\">\n <div className=\"h-6 bg-muted rounded w-1/3\" />\n <div className=\"h-12 bg-muted rounded\" />\n </div>\n ) : settings ? (\n <div className=\"space-y-6\">\n <div>\n <label className=\"text-sm font-medium text-foreground\">\n {t('inbox_ops.settings.forwarding_address', 'Forwarding Address')}\n </label>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('inbox_ops.settings.forwarding_hint', 'Forward email threads to this address to create proposals')}\n </p>\n <div className=\"mt-2 flex items-center gap-2\">\n <div className=\"flex-1 bg-muted rounded-lg px-4 py-3\">\n <code className=\"text-sm font-mono\">{settings.inboxAddress}</code>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"h-11 md:h-9\" onClick={handleCopy}>\n {copied ? <CheckCircle className=\"h-4 w-4 text-green-600\" /> : <Copy className=\"h-4 w-4\" />}\n <span className=\"ml-1\">{copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}</span>\n </Button>\n </div>\n </div>\n\n <div>\n <label className=\"text-sm font-medium text-foreground\">{t('inbox_ops.settings.status', 'Status')}</label>\n <div className=\"mt-1\">\n <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${\n settings.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'\n }`}>\n {settings.isActive ? t('inbox_ops.settings.active', 'Active') : t('inbox_ops.settings.inactive', 'Inactive')}\n </span>\n </div>\n </div>\n\n <div>\n <label htmlFor=\"working-language\" className=\"text-sm font-medium text-foreground\">\n {t('inbox_ops.settings.working_language', 'Working Language')}\n </label>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('inbox_ops.settings.working_language_hint', 'AI summaries and action descriptions will be generated in this language')}\n </p>\n <select\n id=\"working-language\"\n className=\"mt-2 block w-full sm:w-[200px] h-11 md:h-9 rounded-md border border-input bg-background px-3 text-sm\"\n value={settings.workingLanguage || 'en'}\n onChange={handleLanguageChange}\n disabled={isSavingLanguage}\n >\n {languageOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.settings.not_found', 'No inbox settings found. Settings are created when a new tenant is provisioned.')}</p>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { ArrowLeft, Copy, CheckCircle } from 'lucide-react'\n\nconst LANGUAGE_KEYS = ['en', 'de', 'es', 'pl'] as const\n\nexport default function InboxSettingsPage() {\n const t = useT()\n const { runMutation } = useGuardedMutation<Record<string, unknown>>({\n contextId: 'inbox-ops-settings',\n })\n\n const languageOptions = LANGUAGE_KEYS.map((key) => ({\n value: key,\n label: t(`inbox_ops.settings.language_${key}` as never, key),\n }))\n const [settings, setSettings] = React.useState<{ inboxAddress?: string; isActive?: boolean; workingLanguage?: string } | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [copied, setCopied] = React.useState(false)\n const [isSavingLanguage, setIsSavingLanguage] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setError(null)\n try {\n const result = await apiCall<{ settings: { inboxAddress?: string; isActive?: boolean; workingLanguage?: string } | null }>('/api/inbox_ops/settings')\n if (!cancelled) {\n if (result?.ok && result.result?.settings) {\n setSettings(result.result.settings)\n } else {\n setError(t('inbox_ops.settings.load_failed', 'Failed to load settings'))\n }\n }\n } catch {\n if (!cancelled) setError(t('inbox_ops.settings.load_failed', 'Failed to load settings'))\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [t])\n\n const handleCopy = React.useCallback(() => {\n if (settings?.inboxAddress) {\n navigator.clipboard.writeText(settings.inboxAddress)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n }\n }, [settings])\n\n const handleLanguageChange = React.useCallback(async (event: React.ChangeEvent<HTMLSelectElement>) => {\n const workingLanguage = event.target.value\n setIsSavingLanguage(true)\n const result = await runMutation({\n operation: () => apiCall<{ ok: boolean; settings: { workingLanguage: string } }>('/api/inbox_ops/settings', {\n method: 'PATCH',\n body: JSON.stringify({ workingLanguage }),\n }),\n context: {},\n })\n if (result?.ok && result.result?.ok) {\n setSettings((prev) => prev ? { ...prev, workingLanguage: result.result!.settings.workingLanguage } : prev)\n flash(t('inbox_ops.settings.language_saved', 'Working language updated'), 'success')\n } else {\n flash(t('inbox_ops.settings.language_save_failed', 'Failed to update working language'), 'error')\n }\n setIsSavingLanguage(false)\n }, [t, runMutation])\n\n return (\n <Page>\n <div className=\"flex items-center gap-3 px-3 py-3 md:px-6 md:py-4\">\n <Link href=\"/backend/inbox-ops\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\"><ArrowLeft className=\"h-4 w-4\" /></Button>\n </Link>\n <h1 className=\"text-lg font-semibold\">{t('inbox_ops.settings.title', 'Inbox Settings')}</h1>\n </div>\n\n <PageBody>\n <div className=\"max-w-lg\">\n {isLoading ? (\n <LoadingMessage label={t('inbox_ops.settings.loading', 'Loading settings...')} />\n ) : error ? (\n <ErrorMessage label={error} />\n ) : settings ? (\n <div className=\"space-y-6\">\n <div>\n <label className=\"text-sm font-medium text-foreground\">\n {t('inbox_ops.settings.forwarding_address', 'Forwarding Address')}\n </label>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('inbox_ops.settings.forwarding_hint', 'Forward email threads to this address to create proposals')}\n </p>\n <div className=\"mt-2 flex items-center gap-2\">\n <div className=\"flex-1 bg-muted rounded-lg px-4 py-3\">\n <code className=\"text-sm font-mono\">{settings.inboxAddress}</code>\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className=\"h-11 md:h-9\" onClick={handleCopy}>\n {copied ? <CheckCircle className=\"h-4 w-4 text-green-600\" /> : <Copy className=\"h-4 w-4\" />}\n <span className=\"ml-1\">{copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}</span>\n </Button>\n </div>\n </div>\n\n <div>\n <label className=\"text-sm font-medium text-foreground\">{t('inbox_ops.settings.status', 'Status')}</label>\n <div className=\"mt-1\">\n <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${\n settings.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'\n }`}>\n {settings.isActive ? t('inbox_ops.settings.active', 'Active') : t('inbox_ops.settings.inactive', 'Inactive')}\n </span>\n </div>\n </div>\n\n <div>\n <label htmlFor=\"working-language\" className=\"text-sm font-medium text-foreground\">\n {t('inbox_ops.settings.working_language', 'Working Language')}\n </label>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {t('inbox_ops.settings.working_language_hint', 'AI summaries and action descriptions will be generated in this language')}\n </p>\n <select\n id=\"working-language\"\n className=\"mt-2 block w-full sm:w-[200px] h-11 md:h-9 rounded-md border border-input bg-background px-3 text-sm\"\n value={settings.workingLanguage || 'en'}\n onChange={handleLanguageChange}\n disabled={isSavingLanguage}\n >\n {languageOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">{t('inbox_ops.settings.not_found', 'No inbox settings found. Settings are created when a new tenant is provisioned.')}</p>\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,11 +3,11 @@ const metadata = {
3
3
  requireFeatures: ["inbox_ops.settings.manage"],
4
4
  pageTitle: "Inbox Settings",
5
5
  pageTitleKey: "inbox_ops.nav.settings",
6
- pageGroup: "InboxOps",
6
+ pageGroup: "AI Inbox Actions",
7
7
  pageGroupKey: "inbox_ops.nav.group",
8
8
  navHidden: true,
9
9
  breadcrumb: [
10
- { label: "InboxOps", labelKey: "inbox_ops.nav.group", href: "/backend/inbox-ops" },
10
+ { label: "AI Inbox Actions", labelKey: "inbox_ops.nav.group", href: "/backend/inbox-ops" },
11
11
  { label: "Settings", labelKey: "inbox_ops.nav.settings" }
12
12
  ]
13
13
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/inbox_ops/backend/inbox-ops/settings/page.meta.ts"],
4
- "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['inbox_ops.settings.manage'],\n pageTitle: 'Inbox Settings',\n pageTitleKey: 'inbox_ops.nav.settings',\n pageGroup: 'InboxOps',\n pageGroupKey: 'inbox_ops.nav.group',\n navHidden: true,\n breadcrumb: [\n { label: 'InboxOps', labelKey: 'inbox_ops.nav.group', href: '/backend/inbox-ops' },\n { label: 'Settings', labelKey: 'inbox_ops.nav.settings' },\n ],\n}\n"],
5
- "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,2BAA2B;AAAA,EAC7C,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,OAAO,YAAY,UAAU,uBAAuB,MAAM,qBAAqB;AAAA,IACjF,EAAE,OAAO,YAAY,UAAU,yBAAyB;AAAA,EAC1D;AACF;",
4
+ "sourcesContent": ["export const metadata = {\n requireAuth: true,\n requireFeatures: ['inbox_ops.settings.manage'],\n pageTitle: 'Inbox Settings',\n pageTitleKey: 'inbox_ops.nav.settings',\n pageGroup: 'AI Inbox Actions',\n pageGroupKey: 'inbox_ops.nav.group',\n navHidden: true,\n breadcrumb: [\n { label: 'AI Inbox Actions', labelKey: 'inbox_ops.nav.group', href: '/backend/inbox-ops' },\n { label: 'Settings', labelKey: 'inbox_ops.nav.settings' },\n ],\n}\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,aAAa;AAAA,EACb,iBAAiB,CAAC,2BAA2B;AAAA,EAC7C,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd,WAAW;AAAA,EACX,YAAY;AAAA,IACV,EAAE,OAAO,oBAAoB,UAAU,uBAAuB,MAAM,qBAAqB;AAAA,IACzF,EAAE,OAAO,YAAY,UAAU,yBAAyB;AAAA,EAC1D;AACF;",
6
6
  "names": []
7
7
  }
@@ -17,6 +17,44 @@ import {
17
17
  Activity,
18
18
  ShoppingBag
19
19
  } from "lucide-react";
20
+ import { hasContactNameIssue } from "../../lib/contactValidation.js";
21
+ function useDiscrepancyDescriptions() {
22
+ const t = useT();
23
+ const translations = {
24
+ "inbox_ops.discrepancy.desc.no_channel": t("inbox_ops.discrepancy.desc.no_channel", "No sales channel available. Create a channel in Sales settings before accepting this order."),
25
+ "inbox_ops.discrepancy.desc.no_currency": t("inbox_ops.discrepancy.desc.no_currency", "No currency could be resolved for this order. Set a currency code or configure a sales channel with a default currency."),
26
+ "inbox_ops.discrepancy.desc.product_not_matched": t("inbox_ops.discrepancy.desc.product_not_matched", "Product could not be matched to any catalog product"),
27
+ "inbox_ops.discrepancy.desc.no_matching_contact": t("inbox_ops.discrepancy.desc.no_matching_contact", "No matching contact found"),
28
+ "inbox_ops.discrepancy.desc.draft_reply_no_contact": t("inbox_ops.discrepancy.desc.draft_reply_no_contact", "Draft reply target has no matching contact. Create the contact first."),
29
+ "inbox_ops.discrepancy.desc.duplicate_order_reference": t("inbox_ops.discrepancy.desc.duplicate_order_reference", "An order with this customer reference already exists")
30
+ };
31
+ return (description, foundValue) => {
32
+ const translated = translations[description];
33
+ if (!translated) return description;
34
+ if (foundValue && (description === "inbox_ops.discrepancy.desc.product_not_matched" || description === "inbox_ops.discrepancy.desc.no_matching_contact")) {
35
+ return `${translated}: ${foundValue}`;
36
+ }
37
+ return translated;
38
+ };
39
+ }
40
+ function useActionDescriptionResolver() {
41
+ const t = useT();
42
+ return (description, payload) => {
43
+ if (!description.startsWith("inbox_ops.action.desc.")) return description;
44
+ const name = payload.name || payload.contactName || "";
45
+ const email = payload.email || payload.emailAddress || "";
46
+ const title = payload.title || "";
47
+ const toName = payload.toName || payload.to || "";
48
+ const subject = payload.subject || "";
49
+ const translations = {
50
+ "inbox_ops.action.desc.create_contact": t("inbox_ops.action.desc.create_contact", "Create contact for {name} ({email})").replace("{name}", name).replace("{email}", email),
51
+ "inbox_ops.action.desc.link_contact": t("inbox_ops.action.desc.link_contact", "Link {name} ({email}) to existing contact").replace("{name}", name).replace("{email}", email),
52
+ "inbox_ops.action.desc.create_product": t("inbox_ops.action.desc.create_product", 'Create catalog product "{title}"').replace("{title}", title),
53
+ "inbox_ops.action.desc.draft_reply": t("inbox_ops.action.desc.draft_reply", "Draft reply to {toName}: {subject}").replace("{toName}", toName).replace("{subject}", subject)
54
+ };
55
+ return translations[description] || description;
56
+ };
57
+ }
20
58
  const ACTION_TYPE_ICONS = {
21
59
  create_order: Package,
22
60
  create_quote: FileText,
@@ -157,14 +195,16 @@ function ActionCard({
157
195
  onReject,
158
196
  onRetry,
159
197
  onEdit,
160
- translatedDescription
198
+ translatedDescription,
199
+ resolveDiscrepancyDescription
161
200
  }) {
162
201
  const t = useT();
163
202
  const Icon = ACTION_TYPE_ICONS[action.actionType] || Package;
164
203
  const label = actionTypeLabels[action.actionType] || action.actionType;
204
+ const resolveActionDescription = useActionDescriptionResolver();
165
205
  const actionDiscrepancies = discrepancies.filter((d) => d.actionId === action.id && !d.resolved);
166
206
  const hasBlockingDiscrepancies = actionDiscrepancies.some((d) => d.severity === "error");
167
- const displayDescription = translatedDescription || action.description;
207
+ const displayDescription = translatedDescription || resolveActionDescription(action.description, action.payload);
168
208
  if (action.status === "executed") {
169
209
  return /* @__PURE__ */ jsxs("div", { className: "border rounded-lg p-3 md:p-4 bg-green-50 dark:bg-green-950/20", children: [
170
210
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
@@ -173,8 +213,7 @@ function ActionCard({
173
213
  ] }),
174
214
  /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: displayDescription }),
175
215
  action.createdEntityId && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs("span", { className: "text-xs text-green-600", children: [
176
- "Created ",
177
- action.createdEntityType,
216
+ t("inbox_ops.action.created_entity", "Created {type}").replace("{type}", action.createdEntityType || ""),
178
217
  " \xB7 ",
179
218
  action.executedAt && new Date(action.executedAt).toLocaleString()
180
219
  ] }) })
@@ -203,6 +242,7 @@ function ActionCard({
203
242
  /* @__PURE__ */ jsxs(
204
243
  Button,
205
244
  {
245
+ type: "button",
206
246
  size: "sm",
207
247
  className: "h-11 md:h-9",
208
248
  onClick: () => onRetry(action.id),
@@ -215,6 +255,7 @@ function ActionCard({
215
255
  /* @__PURE__ */ jsxs(
216
256
  Button,
217
257
  {
258
+ type: "button",
218
259
  variant: "outline",
219
260
  size: "sm",
220
261
  className: "h-11 md:h-9",
@@ -228,6 +269,7 @@ function ActionCard({
228
269
  /* @__PURE__ */ jsxs(
229
270
  Button,
230
271
  {
272
+ type: "button",
231
273
  variant: "outline",
232
274
  size: "sm",
233
275
  className: "h-11 md:h-9",
@@ -241,6 +283,7 @@ function ActionCard({
241
283
  ] })
242
284
  ] });
243
285
  }
286
+ const hasNameIssue = hasContactNameIssue(action);
244
287
  return /* @__PURE__ */ jsxs("div", { className: "border rounded-lg p-3 md:p-4", children: [
245
288
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
246
289
  /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-primary flex-shrink-0" }),
@@ -253,28 +296,35 @@ function ActionCard({
253
296
  actionDiscrepancies.length > 0 && /* @__PURE__ */ jsx("div", { className: "mb-3 space-y-1", children: actionDiscrepancies.map((d) => /* @__PURE__ */ jsxs("div", { className: `flex items-start gap-2 text-xs rounded px-2 py-1.5 ${d.severity === "error" ? "bg-red-50 text-red-700 dark:bg-red-950/20" : "bg-yellow-50 text-yellow-700 dark:bg-yellow-950/20"}`, children: [
254
297
  /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 mt-0.5 flex-shrink-0" }),
255
298
  /* @__PURE__ */ jsxs("div", { children: [
256
- /* @__PURE__ */ jsx("span", { children: d.description }),
299
+ /* @__PURE__ */ jsx("span", { children: resolveDiscrepancyDescription ? resolveDiscrepancyDescription(d.description, d.foundValue) : d.description }),
257
300
  (d.expectedValue || d.foundValue) && /* @__PURE__ */ jsxs("div", { className: "mt-0.5 text-[11px] opacity-80", children: [
258
301
  d.expectedValue && /* @__PURE__ */ jsxs("span", { children: [
259
- "Expected: ",
302
+ t("inbox_ops.discrepancy.expected", "Expected"),
303
+ ": ",
260
304
  d.expectedValue
261
305
  ] }),
262
306
  d.expectedValue && d.foundValue && /* @__PURE__ */ jsx("span", { children: " \xB7 " }),
263
307
  d.foundValue && /* @__PURE__ */ jsxs("span", { children: [
264
- "Found: ",
308
+ t("inbox_ops.discrepancy.found", "Found"),
309
+ ": ",
265
310
  d.foundValue
266
311
  ] })
267
312
  ] })
268
313
  ] })
269
314
  ] }, d.id)) }),
315
+ hasNameIssue && /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-start gap-2 text-xs rounded px-2 py-1.5 bg-amber-50 text-amber-700 dark:bg-amber-950/20 dark:text-amber-300", children: [
316
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 mt-0.5 flex-shrink-0" }),
317
+ /* @__PURE__ */ jsx("span", { children: action.actionType === "link_contact" ? t("inbox_ops.contact.link_name_missing_warning", "Contact name is missing. Please edit and provide a name before accepting.") : t("inbox_ops.contact.name_missing_warning", "First and last name could not be extracted. Please edit before accepting.") })
318
+ ] }),
270
319
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
271
- /* @__PURE__ */ jsx("div", { title: hasBlockingDiscrepancies ? t("inbox_ops.action.accept_blocked", "Resolve errors before accepting") : void 0, children: /* @__PURE__ */ jsxs(
320
+ /* @__PURE__ */ jsx("div", { title: hasNameIssue ? action.actionType === "link_contact" ? t("inbox_ops.contact.link_name_missing_warning", "Contact name is missing. Please edit and provide a name before accepting.") : t("inbox_ops.contact.name_missing_warning", "First and last name could not be extracted. Please edit before accepting.") : hasBlockingDiscrepancies ? t("inbox_ops.action.accept_blocked", "Resolve errors before accepting") : void 0, children: /* @__PURE__ */ jsxs(
272
321
  Button,
273
322
  {
323
+ type: "button",
274
324
  size: "sm",
275
325
  className: "h-11 md:h-9",
276
326
  onClick: () => onAccept(action.id),
277
- disabled: hasBlockingDiscrepancies,
327
+ disabled: hasBlockingDiscrepancies || hasNameIssue,
278
328
  children: [
279
329
  /* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4 mr-1" }),
280
330
  t("inbox_ops.action.accept", "Accept")
@@ -284,6 +334,7 @@ function ActionCard({
284
334
  /* @__PURE__ */ jsxs(
285
335
  Button,
286
336
  {
337
+ type: "button",
287
338
  variant: "outline",
288
339
  size: "sm",
289
340
  className: "h-11 md:h-9",
@@ -297,6 +348,7 @@ function ActionCard({
297
348
  /* @__PURE__ */ jsxs(
298
349
  Button,
299
350
  {
351
+ type: "button",
300
352
  variant: "outline",
301
353
  size: "sm",
302
354
  className: "h-11 md:h-9",
@@ -313,6 +365,9 @@ function ActionCard({
313
365
  export {
314
366
  ActionCard,
315
367
  ConfidenceBadge,
316
- useActionTypeLabels
368
+ hasContactNameIssue,
369
+ useActionDescriptionResolver,
370
+ useActionTypeLabels,
371
+ useDiscrepancyDescriptions
317
372
  };
318
373
  //# sourceMappingURL=ActionCard.js.map