@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.
- package/dist/modules/catalog/inbox-actions.js +51 -0
- package/dist/modules/catalog/inbox-actions.js.map +7 -0
- package/dist/modules/customers/inbox-actions.js +230 -0
- package/dist/modules/customers/inbox-actions.js.map +7 -0
- package/dist/modules/inbox_ops/api/emails/[id]/route.js +40 -1
- package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/extract/route.js +87 -0
- package/dist/modules/inbox_ops/api/extract/route.js.map +7 -0
- package/dist/modules/inbox_ops/api/proposals/[id]/translate/route.js +6 -1
- package/dist/modules/inbox_ops/api/proposals/[id]/translate/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/counts/route.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +40 -14
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.meta.js +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.meta.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js +161 -79
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.meta.js +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.meta.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js +109 -62
- package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js.map +3 -3
- package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.js +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +36 -14
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.meta.js +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.meta.js.map +2 -2
- package/dist/modules/inbox_ops/components/proposals/ActionCard.js +65 -10
- package/dist/modules/inbox_ops/components/proposals/ActionCard.js.map +2 -2
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +58 -10
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
- package/dist/modules/inbox_ops/lib/constants.js.map +2 -2
- package/dist/modules/inbox_ops/lib/contactValidation.js +40 -0
- package/dist/modules/inbox_ops/lib/contactValidation.js.map +7 -0
- package/dist/modules/inbox_ops/lib/executionEngine.js +31 -826
- package/dist/modules/inbox_ops/lib/executionEngine.js.map +3 -3
- package/dist/modules/inbox_ops/lib/executionHelpers.js +368 -0
- package/dist/modules/inbox_ops/lib/executionHelpers.js.map +7 -0
- package/dist/modules/inbox_ops/lib/extractionPrompt.js +28 -35
- package/dist/modules/inbox_ops/lib/extractionPrompt.js.map +3 -3
- package/dist/modules/inbox_ops/lib/inbox-actions-generated.d.js +1 -0
- package/dist/modules/inbox_ops/lib/inbox-actions-generated.d.js.map +7 -0
- package/dist/modules/inbox_ops/lib/translationProvider.js +15 -10
- package/dist/modules/inbox_ops/lib/translationProvider.js.map +2 -2
- package/dist/modules/inbox_ops/subscribers/extractionWorker.js +16 -16
- package/dist/modules/inbox_ops/subscribers/extractionWorker.js.map +2 -2
- package/dist/modules/sales/inbox-actions.js +278 -0
- package/dist/modules/sales/inbox-actions.js.map +7 -0
- package/jest.config.cjs +1 -0
- package/jest.mocks/inbox-actions.generated.js +5 -0
- package/package.json +2 -2
- package/src/modules/catalog/inbox-actions.ts +60 -0
- package/src/modules/customers/inbox-actions.ts +285 -0
- package/src/modules/inbox_ops/api/emails/[id]/route.ts +44 -0
- package/src/modules/inbox_ops/api/extract/route.ts +94 -0
- package/src/modules/inbox_ops/api/proposals/[id]/translate/route.ts +6 -1
- package/src/modules/inbox_ops/api/proposals/counts/route.ts +2 -0
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.meta.ts +2 -2
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +43 -13
- package/src/modules/inbox_ops/backend/inbox-ops/page.meta.ts +2 -2
- package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +176 -81
- package/src/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.ts +2 -2
- package/src/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.tsx +122 -68
- package/src/modules/inbox_ops/backend/inbox-ops/settings/page.meta.ts +2 -2
- package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +36 -14
- package/src/modules/inbox_ops/components/proposals/ActionCard.tsx +91 -7
- package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +64 -12
- package/src/modules/inbox_ops/lib/constants.ts +9 -0
- package/src/modules/inbox_ops/lib/contactValidation.ts +54 -0
- package/src/modules/inbox_ops/lib/executionEngine.ts +47 -1060
- package/src/modules/inbox_ops/lib/executionHelpers.ts +527 -0
- package/src/modules/inbox_ops/lib/extractionPrompt.ts +45 -34
- package/src/modules/inbox_ops/lib/inbox-actions-generated.d.ts +11 -0
- package/src/modules/inbox_ops/lib/translationProvider.ts +16 -10
- package/src/modules/inbox_ops/subscribers/extractionWorker.ts +16 -18
- package/src/modules/sales/inbox-actions.ts +359 -0
|
@@ -12,10 +12,10 @@ export const metadata = {
|
|
|
12
12
|
requireFeatures: ['inbox_ops.proposals.view'],
|
|
13
13
|
pageTitle: 'Proposals',
|
|
14
14
|
pageTitleKey: 'inbox_ops.nav.proposals',
|
|
15
|
-
pageGroup: '
|
|
15
|
+
pageGroup: 'AI Inbox Actions',
|
|
16
16
|
pageGroupKey: 'inbox_ops.nav.group',
|
|
17
17
|
pagePriority: 45,
|
|
18
18
|
pageOrder: 100,
|
|
19
19
|
icon: inboxIcon,
|
|
20
|
-
breadcrumb: [{ label: '
|
|
20
|
+
breadcrumb: [{ label: 'AI Inbox Actions', labelKey: 'inbox_ops.nav.group' }],
|
|
21
21
|
} as const
|
|
@@ -5,12 +5,17 @@ import Link from 'next/link'
|
|
|
5
5
|
import { useRouter } from 'next/navigation'
|
|
6
6
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
7
7
|
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
8
|
+
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
8
9
|
import type { ColumnDef } from '@tanstack/react-table'
|
|
10
|
+
import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
|
|
9
11
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
10
12
|
import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
|
|
13
|
+
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
11
14
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
12
15
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
13
|
-
import {
|
|
16
|
+
import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
|
|
17
|
+
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
18
|
+
import { ErrorMessage } from '@open-mercato/ui/backend/detail'
|
|
14
19
|
import { Settings, Inbox, Copy } from 'lucide-react'
|
|
15
20
|
|
|
16
21
|
type ProposalRow = {
|
|
@@ -75,33 +80,48 @@ export default function InboxOpsProposalsPage() {
|
|
|
75
80
|
const t = useT()
|
|
76
81
|
const router = useRouter()
|
|
77
82
|
const scopeVersion = useOrganizationScopeVersion()
|
|
83
|
+
const { confirm, ConfirmDialogElement } = useConfirmDialog()
|
|
84
|
+
const { runMutation } = useGuardedMutation<Record<string, unknown>>({
|
|
85
|
+
contextId: 'inbox-ops-proposals',
|
|
86
|
+
})
|
|
78
87
|
|
|
79
88
|
const [items, setItems] = React.useState<ProposalRow[]>([])
|
|
80
89
|
const [total, setTotal] = React.useState(0)
|
|
81
90
|
const [page, setPage] = React.useState(1)
|
|
82
91
|
const [pageSize] = React.useState(25)
|
|
83
|
-
const [
|
|
92
|
+
const [filterValues, setFilterValues] = React.useState<FilterValues>({})
|
|
84
93
|
const [search, setSearch] = React.useState('')
|
|
85
94
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
95
|
+
const [error, setError] = React.useState<string | null>(null)
|
|
96
|
+
const [initialLoadComplete, setInitialLoadComplete] = React.useState(false)
|
|
86
97
|
const [counts, setCounts] = React.useState<StatusCounts>({ pending: 0, partial: 0, accepted: 0, rejected: 0 })
|
|
87
98
|
const [settings, setSettings] = React.useState<{ inboxAddress?: string } | null>(null)
|
|
88
99
|
const [copied, setCopied] = React.useState(false)
|
|
89
100
|
|
|
101
|
+
const statusFilter = typeof filterValues.status === 'string' ? filterValues.status : undefined
|
|
102
|
+
|
|
90
103
|
const loadProposals = React.useCallback(async () => {
|
|
91
104
|
setIsLoading(true)
|
|
105
|
+
setError(null)
|
|
92
106
|
const params = new URLSearchParams()
|
|
93
107
|
params.set('page', String(page))
|
|
94
108
|
params.set('pageSize', String(pageSize))
|
|
95
109
|
if (statusFilter) params.set('status', statusFilter)
|
|
96
|
-
if (search) params.set('search', search)
|
|
110
|
+
if (search.trim()) params.set('search', search.trim())
|
|
97
111
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
112
|
+
try {
|
|
113
|
+
const result = await apiCall<ProposalListResponse>(`/api/inbox_ops/proposals?${params}`)
|
|
114
|
+
if (result?.ok && result.result?.items) {
|
|
115
|
+
setItems(result.result.items)
|
|
116
|
+
setTotal(result.result.total || 0)
|
|
117
|
+
} else {
|
|
118
|
+
setError(t('inbox_ops.flash.load_failed', 'Failed to load proposals'))
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
setError(t('inbox_ops.flash.load_failed', 'Failed to load proposals'))
|
|
102
122
|
}
|
|
103
123
|
setIsLoading(false)
|
|
104
|
-
}, [page, pageSize, statusFilter, search, scopeVersion])
|
|
124
|
+
}, [page, pageSize, statusFilter, search, scopeVersion, t])
|
|
105
125
|
|
|
106
126
|
const loadCounts = React.useCallback(async () => {
|
|
107
127
|
const result = await apiCall<StatusCounts>('/api/inbox_ops/proposals/counts')
|
|
@@ -114,10 +134,14 @@ export default function InboxOpsProposalsPage() {
|
|
|
114
134
|
}, [scopeVersion])
|
|
115
135
|
|
|
116
136
|
React.useEffect(() => {
|
|
117
|
-
loadProposals()
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}, [
|
|
137
|
+
Promise.all([loadProposals(), loadCounts(), loadSettings()]).then(() => {
|
|
138
|
+
setInitialLoadComplete(true)
|
|
139
|
+
})
|
|
140
|
+
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
|
141
|
+
|
|
142
|
+
React.useEffect(() => {
|
|
143
|
+
if (initialLoadComplete) loadProposals()
|
|
144
|
+
}, [page, statusFilter, search, scopeVersion]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
121
145
|
|
|
122
146
|
const handleCopyAddress = React.useCallback(() => {
|
|
123
147
|
if (settings?.inboxAddress) {
|
|
@@ -127,6 +151,58 @@ export default function InboxOpsProposalsPage() {
|
|
|
127
151
|
}
|
|
128
152
|
}, [settings])
|
|
129
153
|
|
|
154
|
+
const handleRefresh = React.useCallback(() => {
|
|
155
|
+
loadProposals()
|
|
156
|
+
loadCounts()
|
|
157
|
+
}, [loadProposals, loadCounts])
|
|
158
|
+
|
|
159
|
+
const handleFiltersApply = React.useCallback((values: FilterValues) => {
|
|
160
|
+
setFilterValues(values)
|
|
161
|
+
setPage(1)
|
|
162
|
+
}, [])
|
|
163
|
+
|
|
164
|
+
const handleFiltersClear = React.useCallback(() => {
|
|
165
|
+
setFilterValues({})
|
|
166
|
+
setPage(1)
|
|
167
|
+
}, [])
|
|
168
|
+
|
|
169
|
+
const handleRejectProposal = React.useCallback(async (proposalId: string) => {
|
|
170
|
+
const confirmed = await confirm({
|
|
171
|
+
title: t('inbox_ops.action.reject_all', 'Reject Proposal'),
|
|
172
|
+
text: t('inbox_ops.action.reject_all_confirm', 'Reject all pending actions in this proposal?'),
|
|
173
|
+
})
|
|
174
|
+
if (!confirmed) return
|
|
175
|
+
|
|
176
|
+
const result = await runMutation({
|
|
177
|
+
operation: () => apiCall<{ ok: boolean }>(
|
|
178
|
+
`/api/inbox_ops/proposals/${proposalId}/reject`,
|
|
179
|
+
{ method: 'POST' },
|
|
180
|
+
),
|
|
181
|
+
context: {},
|
|
182
|
+
})
|
|
183
|
+
if (result?.ok && result.result?.ok) {
|
|
184
|
+
flash(t('inbox_ops.action.proposal_rejected', 'Proposal rejected'), 'success')
|
|
185
|
+
loadProposals()
|
|
186
|
+
loadCounts()
|
|
187
|
+
} else {
|
|
188
|
+
flash(t('inbox_ops.flash.action_reject_failed', 'Failed to reject'), 'error')
|
|
189
|
+
}
|
|
190
|
+
}, [confirm, t, loadProposals, loadCounts, runMutation])
|
|
191
|
+
|
|
192
|
+
const filters = React.useMemo<FilterDef[]>(() => [
|
|
193
|
+
{
|
|
194
|
+
id: 'status',
|
|
195
|
+
label: t('inbox_ops.list.filters.status', 'Status'),
|
|
196
|
+
type: 'select',
|
|
197
|
+
options: [
|
|
198
|
+
{ value: 'pending', label: `${t('inbox_ops.status.pending', 'Pending')} (${counts.pending})` },
|
|
199
|
+
{ value: 'partial', label: `${t('inbox_ops.status.partial', 'Partial')} (${counts.partial})` },
|
|
200
|
+
{ value: 'accepted', label: `${t('inbox_ops.status.accepted', 'Accepted')} (${counts.accepted})` },
|
|
201
|
+
{ value: 'rejected', label: `${t('inbox_ops.status.rejected', 'Rejected')} (${counts.rejected})` },
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
], [t, counts])
|
|
205
|
+
|
|
130
206
|
const columns: ColumnDef<ProposalRow>[] = React.useMemo(() => [
|
|
131
207
|
{
|
|
132
208
|
accessorKey: 'summary',
|
|
@@ -152,7 +228,7 @@ export default function InboxOpsProposalsPage() {
|
|
|
152
228
|
},
|
|
153
229
|
{
|
|
154
230
|
id: 'actions_count',
|
|
155
|
-
header: t('inbox_ops.
|
|
231
|
+
header: t('inbox_ops.list.progress', 'Progress'),
|
|
156
232
|
cell: ({ row }) => {
|
|
157
233
|
const pending = row.original.pendingActionCount ?? 0
|
|
158
234
|
const total = row.original.actionCount ?? 0
|
|
@@ -183,83 +259,102 @@ export default function InboxOpsProposalsPage() {
|
|
|
183
259
|
], [t])
|
|
184
260
|
|
|
185
261
|
const totalCount = counts.pending + counts.partial + counts.accepted + counts.rejected
|
|
186
|
-
const isEmpty = totalCount === 0 && !isLoading
|
|
187
262
|
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
263
|
+
const emptyStateContent = initialLoadComplete && totalCount === 0 ? (
|
|
264
|
+
<div className="flex flex-col items-center justify-center py-16 text-center">
|
|
265
|
+
<Inbox className="h-12 w-12 text-muted-foreground mb-4" />
|
|
266
|
+
<h2 className="text-lg font-semibold mb-2">{t('inbox_ops.empty.title', 'Forward emails to start')}</h2>
|
|
267
|
+
{settings?.inboxAddress && (
|
|
268
|
+
<div className="mt-4 flex items-center gap-2 bg-muted rounded-lg px-4 py-3">
|
|
269
|
+
<code className="text-sm font-mono">{settings.inboxAddress}</code>
|
|
270
|
+
<Button type="button" variant="outline" size="sm" onClick={handleCopyAddress}>
|
|
271
|
+
<Copy className="h-4 w-4" />
|
|
272
|
+
{copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}
|
|
273
|
+
</Button>
|
|
274
|
+
</div>
|
|
275
|
+
)}
|
|
276
|
+
<ol className="mt-6 text-sm text-muted-foreground text-left space-y-2">
|
|
277
|
+
<li>1. {t('inbox_ops.empty.step1', 'Forward any email thread to this address')}</li>
|
|
278
|
+
<li>2. {t('inbox_ops.empty.step2', "We'll analyze it and propose actions")}</li>
|
|
279
|
+
<li>3. {t('inbox_ops.empty.step3', 'Review and accept with one click')}</li>
|
|
280
|
+
</ol>
|
|
281
|
+
</div>
|
|
282
|
+
) : undefined
|
|
283
|
+
|
|
284
|
+
if (error && !initialLoadComplete) {
|
|
285
|
+
return (
|
|
286
|
+
<Page>
|
|
287
|
+
<PageBody>
|
|
288
|
+
<ErrorMessage label={error} />
|
|
289
|
+
</PageBody>
|
|
290
|
+
</Page>
|
|
291
|
+
)
|
|
292
|
+
}
|
|
195
293
|
|
|
196
294
|
return (
|
|
197
295
|
<Page>
|
|
198
|
-
<div className="flex items-center justify-between px-3 py-3 md:px-6 md:py-4">
|
|
199
|
-
<h1 className="text-lg font-semibold">{t('inbox_ops.title', 'InboxOps')}</h1>
|
|
200
|
-
<Link href="/backend/inbox-ops/settings">
|
|
201
|
-
<Button variant="outline" size="sm">
|
|
202
|
-
<Settings className="h-4 w-4" />
|
|
203
|
-
<span className="hidden md:inline ml-1">{t('inbox_ops.settings.title', 'Settings')}</span>
|
|
204
|
-
</Button>
|
|
205
|
-
</Link>
|
|
206
|
-
</div>
|
|
207
|
-
|
|
208
296
|
<PageBody>
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
297
|
+
<DataTable<ProposalRow>
|
|
298
|
+
title={t('inbox_ops.title', 'AI Inbox Actions')}
|
|
299
|
+
refreshButton={{
|
|
300
|
+
label: t('inbox_ops.list.actions.refresh', 'Refresh'),
|
|
301
|
+
onRefresh: handleRefresh,
|
|
302
|
+
}}
|
|
303
|
+
actions={(
|
|
304
|
+
<div className="flex items-center gap-2">
|
|
305
|
+
{settings?.inboxAddress && (
|
|
306
|
+
<Button type="button" variant="outline" size="sm" onClick={handleCopyAddress}>
|
|
217
307
|
<Copy className="h-4 w-4" />
|
|
218
|
-
|
|
308
|
+
<span className="hidden md:inline ml-1">
|
|
309
|
+
{copied ? t('inbox_ops.settings.copied', 'Copied') : t('inbox_ops.settings.copy', 'Copy')}
|
|
310
|
+
</span>
|
|
219
311
|
</Button>
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
</div>
|
|
228
|
-
) : (
|
|
229
|
-
<>
|
|
230
|
-
<div className="flex items-center gap-2 px-3 py-2 md:px-0 overflow-x-auto">
|
|
231
|
-
{tabs.map((tab) => (
|
|
232
|
-
<Button
|
|
233
|
-
key={tab.value ?? 'all'}
|
|
234
|
-
variant={statusFilter === tab.value ? 'default' : 'outline'}
|
|
235
|
-
size="sm"
|
|
236
|
-
onClick={() => { setStatusFilter(tab.value); setPage(1) }}
|
|
237
|
-
>
|
|
238
|
-
{tab.label}
|
|
239
|
-
</Button>
|
|
240
|
-
))}
|
|
312
|
+
)}
|
|
313
|
+
<Button variant="outline" size="sm" asChild>
|
|
314
|
+
<Link href="/backend/inbox-ops/settings">
|
|
315
|
+
<Settings className="h-4 w-4" />
|
|
316
|
+
<span className="hidden md:inline ml-1">{t('inbox_ops.list.actions.settings', 'Settings')}</span>
|
|
317
|
+
</Link>
|
|
318
|
+
</Button>
|
|
241
319
|
</div>
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
320
|
+
)}
|
|
321
|
+
columns={columns}
|
|
322
|
+
data={items}
|
|
323
|
+
searchValue={search}
|
|
324
|
+
onSearchChange={(value) => { setSearch(value); setPage(1) }}
|
|
325
|
+
searchPlaceholder={t('inbox_ops.list.searchPlaceholder', 'Search proposals...')}
|
|
326
|
+
filters={filters}
|
|
327
|
+
filterValues={filterValues}
|
|
328
|
+
onFiltersApply={handleFiltersApply}
|
|
329
|
+
onFiltersClear={handleFiltersClear}
|
|
330
|
+
onRowClick={(row) => router.push(`/backend/inbox-ops/proposals/${row.id}`)}
|
|
331
|
+
rowActions={(row) => (
|
|
332
|
+
<RowActions items={[
|
|
333
|
+
{
|
|
334
|
+
id: 'view',
|
|
335
|
+
label: t('inbox_ops.list.actions.view', 'View'),
|
|
336
|
+
onSelect: () => router.push(`/backend/inbox-ops/proposals/${row.id}`),
|
|
337
|
+
},
|
|
338
|
+
...(row.status === 'pending' || row.status === 'partial' ? [{
|
|
339
|
+
id: 'reject',
|
|
340
|
+
label: t('inbox_ops.list.actions.reject', 'Reject'),
|
|
341
|
+
destructive: true,
|
|
342
|
+
onSelect: () => handleRejectProposal(row.id),
|
|
343
|
+
}] : []),
|
|
344
|
+
]} />
|
|
345
|
+
)}
|
|
346
|
+
pagination={{
|
|
347
|
+
page,
|
|
348
|
+
pageSize,
|
|
349
|
+
total,
|
|
350
|
+
totalPages: Math.ceil(total / pageSize),
|
|
351
|
+
onPageChange: setPage,
|
|
352
|
+
}}
|
|
353
|
+
isLoading={isLoading}
|
|
354
|
+
emptyState={emptyStateContent}
|
|
355
|
+
/>
|
|
262
356
|
</PageBody>
|
|
357
|
+
{ConfirmDialogElement}
|
|
263
358
|
</Page>
|
|
264
359
|
)
|
|
265
360
|
}
|
|
@@ -3,11 +3,11 @@ export const metadata = {
|
|
|
3
3
|
requireFeatures: ['inbox_ops.proposals.view'],
|
|
4
4
|
pageTitle: 'Proposal',
|
|
5
5
|
pageTitleKey: 'inbox_ops.nav.proposal_detail',
|
|
6
|
-
pageGroup: '
|
|
6
|
+
pageGroup: 'AI Inbox Actions',
|
|
7
7
|
pageGroupKey: 'inbox_ops.nav.group',
|
|
8
8
|
navHidden: true,
|
|
9
9
|
breadcrumb: [
|
|
10
|
-
{ label: '
|
|
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
|
}
|