@open-mercato/core 0.10.1-develop.7883.1.44ac2125f5 → 0.10.1-develop.7888.1.96c8c2bd5f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/warranty_claims/backend/components/ReceivingPanel.js +5 -2
- package/dist/modules/warranty_claims/backend/components/ReceivingPanel.js.map +2 -2
- package/dist/modules/warranty_claims/backend/components/ReturnLabelPanel.js +1 -1
- package/dist/modules/warranty_claims/backend/components/ReturnLabelPanel.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js +44 -15
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js +31 -5
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js.map +3 -3
- package/dist/modules/warranty_claims/lib/claimLines.js +20 -0
- package/dist/modules/warranty_claims/lib/claimLines.js.map +7 -0
- package/package.json +7 -7
- package/src/modules/warranty_claims/backend/components/ReceivingPanel.tsx +6 -3
- package/src/modules/warranty_claims/backend/components/ReturnLabelPanel.tsx +2 -2
- package/src/modules/warranty_claims/backend/warranty_claims/[id]/page.tsx +42 -16
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.tsx +33 -5
- package/src/modules/warranty_claims/lib/claimLines.ts +22 -0
|
@@ -9,6 +9,7 @@ import { hasFeature } from '@open-mercato/shared/security/features'
|
|
|
9
9
|
import { parseBooleanFromUnknown } from '@open-mercato/shared/lib/boolean'
|
|
10
10
|
import type { TranslateFn, TranslateParams } from '@open-mercato/shared/lib/i18n/context'
|
|
11
11
|
import { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'
|
|
12
|
+
import { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'
|
|
12
13
|
import { formatDateTime, formatRelativeTime } from '@open-mercato/shared/lib/time'
|
|
13
14
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
14
15
|
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
@@ -67,6 +68,7 @@ import {
|
|
|
67
68
|
import { AiAssessButtons } from '../../components/AiAssessButtons'
|
|
68
69
|
import { useUserDisplayNames } from '../../components/useUserDisplayNames'
|
|
69
70
|
import { ReceivingPanel } from '../../components/ReceivingPanel'
|
|
71
|
+
import { loadClaimLines } from '../../../lib/claimLines'
|
|
70
72
|
import { ReturnLabelPanel } from '../../components/ReturnLabelPanel'
|
|
71
73
|
import { VendorRecoverySuggestionsPanel } from '../../components/VendorRecoverySuggestionsPanel'
|
|
72
74
|
import { ClaimStageProgress } from '../../components/ClaimStageProgress'
|
|
@@ -929,11 +931,7 @@ export default function WarrantyClaimDetailPage({ params }: { params?: { id?: st
|
|
|
929
931
|
undefined,
|
|
930
932
|
{ fallback: { items: [] }, errorMessage: t('warranty_claims.detail.error.load') },
|
|
931
933
|
),
|
|
932
|
-
|
|
933
|
-
`/api/warranty_claims/lines?claimId=${encodeURIComponent(id)}&page=1&pageSize=100`,
|
|
934
|
-
undefined,
|
|
935
|
-
{ fallback: { items: [] }, errorMessage: t('warranty_claims.detail.error.loadLines') },
|
|
936
|
-
),
|
|
934
|
+
loadClaimLines(id, t('warranty_claims.detail.error.loadLines')),
|
|
937
935
|
readApiResultOrThrow<{ items?: unknown[] }>(
|
|
938
936
|
`/api/warranty_claims/events?claimId=${encodeURIComponent(id)}`,
|
|
939
937
|
undefined,
|
|
@@ -1427,6 +1425,39 @@ export default function WarrantyClaimDetailPage({ params }: { params?: { id?: st
|
|
|
1427
1425
|
}
|
|
1428
1426
|
}, [claim, confirm, currentStatus, featureAccess.claimDelete, loadData, mutationContext, router, runMutation, t])
|
|
1429
1427
|
|
|
1428
|
+
const deleteLine = React.useCallback(async (line: ClaimLine) => {
|
|
1429
|
+
if (!claim || !linesInlineEditable) return
|
|
1430
|
+
const confirmed = await confirm({
|
|
1431
|
+
title: t('warranty_claims.form.lines.remove'),
|
|
1432
|
+
variant: 'destructive',
|
|
1433
|
+
})
|
|
1434
|
+
if (!confirmed) return
|
|
1435
|
+
const payload = { id: line.id, claimId: claim.id }
|
|
1436
|
+
try {
|
|
1437
|
+
await runMutation({
|
|
1438
|
+
operation: () => withScopedApiRequestHeaders(
|
|
1439
|
+
buildOptimisticLockHeader(line.updatedAt),
|
|
1440
|
+
() => deleteCrud('warranty_claims/lines', {
|
|
1441
|
+
body: payload,
|
|
1442
|
+
errorMessage: t('warranty_claims.detail.lines.error.delete'),
|
|
1443
|
+
}),
|
|
1444
|
+
),
|
|
1445
|
+
context: { ...mutationContext, resourceKind: 'warranty_claims.claim_line', resourceId: line.id },
|
|
1446
|
+
mutationPayload: payload,
|
|
1447
|
+
})
|
|
1448
|
+
flash(t('warranty_claims.detail.lines.flash.deleted'), 'success')
|
|
1449
|
+
await loadData()
|
|
1450
|
+
} catch (err) {
|
|
1451
|
+
if (surfaceRecordConflict(err, t, { onRefresh: loadData })) return
|
|
1452
|
+
getTelemetryRuntime()?.reportError(err, {
|
|
1453
|
+
module: 'warranty_claims',
|
|
1454
|
+
code: 'warranty_claims.line_delete_failed',
|
|
1455
|
+
attributes: { claimId: claim.id, lineId: line.id },
|
|
1456
|
+
})
|
|
1457
|
+
flash(translateErrorMessage(err, t, 'warranty_claims.detail.lines.error.delete'), 'error')
|
|
1458
|
+
}
|
|
1459
|
+
}, [claim, confirm, linesInlineEditable, loadData, mutationContext, runMutation, t])
|
|
1460
|
+
|
|
1430
1461
|
const headerMoreActions = React.useMemo(() => {
|
|
1431
1462
|
if (!claim) return []
|
|
1432
1463
|
const items = [
|
|
@@ -2090,6 +2121,12 @@ export default function WarrantyClaimDetailPage({ params }: { params?: { id?: st
|
|
|
2090
2121
|
label: t('warranty_claims.detail.lines.edit'),
|
|
2091
2122
|
onSelect: () => setLineDialog({ mode: 'edit', line }),
|
|
2092
2123
|
},
|
|
2124
|
+
{
|
|
2125
|
+
id: 'delete',
|
|
2126
|
+
label: t('warranty_claims.form.lines.remove'),
|
|
2127
|
+
destructive: true,
|
|
2128
|
+
onSelect: () => { void deleteLine(line) },
|
|
2129
|
+
},
|
|
2093
2130
|
]}
|
|
2094
2131
|
/>
|
|
2095
2132
|
) : undefined}
|
|
@@ -2533,17 +2570,6 @@ export default function WarrantyClaimDetailPage({ params }: { params?: { id?: st
|
|
|
2533
2570
|
setLineDialog(null)
|
|
2534
2571
|
await loadData()
|
|
2535
2572
|
}}
|
|
2536
|
-
onDelete={lineDialog?.mode === 'edit'
|
|
2537
|
-
? async () => {
|
|
2538
|
-
await deleteCrud('warranty_claims/lines', {
|
|
2539
|
-
body: { id: lineDialog.line.id, claimId: claim.id },
|
|
2540
|
-
errorMessage: t('warranty_claims.detail.lines.error.delete'),
|
|
2541
|
-
})
|
|
2542
|
-
flash(t('warranty_claims.detail.lines.flash.deleted'), 'success')
|
|
2543
|
-
setLineDialog(null)
|
|
2544
|
-
await loadData()
|
|
2545
|
-
}
|
|
2546
|
-
: undefined}
|
|
2547
2573
|
/>
|
|
2548
2574
|
</DialogContent>
|
|
2549
2575
|
</Dialog>
|
|
@@ -5,6 +5,7 @@ import Link from 'next/link'
|
|
|
5
5
|
import { ArrowLeft, Check, CircleAlert, Info, Paperclip, RefreshCw, ShieldCheck, Trash2, TriangleAlert } from 'lucide-react'
|
|
6
6
|
import { replaceWithPageReload } from '@open-mercato/core/modules/portal/lib/navigation'
|
|
7
7
|
import { useLocale, useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
8
|
+
import { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'
|
|
8
9
|
import { localizeDictionaryLabel } from '@open-mercato/core/modules/warranty_claims/lib/dictionaryLabels'
|
|
9
10
|
import { formatQuantity } from '@open-mercato/core/modules/warranty_claims/lib/quantity'
|
|
10
11
|
import {
|
|
@@ -30,6 +31,7 @@ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/u
|
|
|
30
31
|
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
31
32
|
import { usePortalContext } from '@open-mercato/ui/portal/PortalContext'
|
|
32
33
|
import { PortalEmptyState } from '@open-mercato/ui/portal/components/PortalEmptyState'
|
|
34
|
+
import { usePortalAppEvent } from '@open-mercato/ui/portal/hooks/usePortalAppEvent'
|
|
33
35
|
|
|
34
36
|
type Props = { params: { orgSlug: string; id: string } }
|
|
35
37
|
|
|
@@ -357,6 +359,8 @@ export default function WarrantyClaimPortalDetailPage({ params }: Props) {
|
|
|
357
359
|
const [actionSubmitting, setActionSubmitting] = React.useState(false)
|
|
358
360
|
const replacementInputRef = React.useRef<HTMLInputElement | null>(null)
|
|
359
361
|
const replacementTargetRef = React.useRef<PortalAttachment | null>(null)
|
|
362
|
+
const claimRequestSequenceRef = React.useRef(0)
|
|
363
|
+
const eventsRequestSequenceRef = React.useRef(0)
|
|
360
364
|
|
|
361
365
|
// Leaving an authenticated page for the login page crosses the public/authenticated
|
|
362
366
|
// boundary, so it must be a full page load — see `replaceWithPageReload`.
|
|
@@ -367,8 +371,11 @@ export default function WarrantyClaimPortalDetailPage({ params }: Props) {
|
|
|
367
371
|
}, [loading, user, params.orgSlug])
|
|
368
372
|
|
|
369
373
|
const refreshEvents = React.useCallback(async (claimId: string) => {
|
|
374
|
+
const requestSequence = ++eventsRequestSequenceRef.current
|
|
370
375
|
const result = await apiCall<EventResponse>(`/api/warranty_claims/portal/events?claimId=${encodeURIComponent(claimId)}`)
|
|
371
|
-
if (result.ok && result.result)
|
|
376
|
+
if (requestSequence === eventsRequestSequenceRef.current && result.ok && result.result) {
|
|
377
|
+
setEvents(result.result.items)
|
|
378
|
+
}
|
|
372
379
|
}, [])
|
|
373
380
|
|
|
374
381
|
const refreshAttachments = React.useCallback(async (claimId: string) => {
|
|
@@ -376,12 +383,14 @@ export default function WarrantyClaimPortalDetailPage({ params }: Props) {
|
|
|
376
383
|
if (result.ok && result.result) setAttachments(result.result.items)
|
|
377
384
|
}, [])
|
|
378
385
|
|
|
379
|
-
const loadClaim = React.useCallback(async () => {
|
|
380
|
-
|
|
386
|
+
const loadClaim = React.useCallback(async (showLoading = true) => {
|
|
387
|
+
const requestSequence = ++claimRequestSequenceRef.current
|
|
388
|
+
if (showLoading) setIsLoading(true)
|
|
381
389
|
setError(null)
|
|
382
390
|
setNotFound(false)
|
|
383
391
|
try {
|
|
384
392
|
const detail = await apiCall<ClaimResponse>(`/api/warranty_claims/portal/claims/${encodeURIComponent(params.id)}`)
|
|
393
|
+
if (requestSequence !== claimRequestSequenceRef.current) return
|
|
385
394
|
if (detail.status === 404) {
|
|
386
395
|
setClaim(null)
|
|
387
396
|
setNotFound(true)
|
|
@@ -397,9 +406,11 @@ export default function WarrantyClaimPortalDetailPage({ params }: Props) {
|
|
|
397
406
|
refreshAttachments(detail.result.item.id),
|
|
398
407
|
])
|
|
399
408
|
} catch {
|
|
400
|
-
|
|
409
|
+
if (requestSequence === claimRequestSequenceRef.current) {
|
|
410
|
+
setError(t('warranty_claims.portal.detail.loadError'))
|
|
411
|
+
}
|
|
401
412
|
} finally {
|
|
402
|
-
setIsLoading(false)
|
|
413
|
+
if (requestSequence === claimRequestSequenceRef.current) setIsLoading(false)
|
|
403
414
|
}
|
|
404
415
|
}, [params.id, refreshAttachments, refreshEvents, t])
|
|
405
416
|
|
|
@@ -407,6 +418,23 @@ export default function WarrantyClaimPortalDetailPage({ params }: Props) {
|
|
|
407
418
|
if (user) void loadClaim()
|
|
408
419
|
}, [loadClaim, user])
|
|
409
420
|
|
|
421
|
+
usePortalAppEvent('warranty_claims.claim.portal_status_changed', (event) => {
|
|
422
|
+
if (!user || event.payload.claimId !== params.id) return
|
|
423
|
+
void loadClaim(false)
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
usePortalAppEvent('warranty_claims.claim.comment_added', (event) => {
|
|
427
|
+
if (!user || event.payload.claimId !== params.id) return
|
|
428
|
+
void refreshEvents(params.id).catch((error) => {
|
|
429
|
+
getTelemetryRuntime()?.reportError(error, {
|
|
430
|
+
module: 'warranty_claims',
|
|
431
|
+
code: 'warranty_claims.portal_timeline_refresh_failed',
|
|
432
|
+
attributes: { claimId: params.id },
|
|
433
|
+
})
|
|
434
|
+
setError(t('warranty_claims.portal.detail.loadError'))
|
|
435
|
+
})
|
|
436
|
+
})
|
|
437
|
+
|
|
410
438
|
React.useEffect(() => {
|
|
411
439
|
if (!user) return
|
|
412
440
|
let cancelled = false
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
|
|
2
|
+
|
|
3
|
+
type ClaimLinesPage = {
|
|
4
|
+
items?: unknown[]
|
|
5
|
+
totalPages?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function loadClaimLines(claimId: string, errorMessage: string): Promise<{ items: unknown[] }> {
|
|
9
|
+
const readPage = (page: number) => readApiResultOrThrow<ClaimLinesPage>(
|
|
10
|
+
`/api/warranty_claims/lines?claimId=${encodeURIComponent(claimId)}&page=${page}&pageSize=100&sortField=lineNo&sortDir=asc`,
|
|
11
|
+
undefined,
|
|
12
|
+
{ fallback: { items: [] }, errorMessage },
|
|
13
|
+
)
|
|
14
|
+
const firstPage = await readPage(1)
|
|
15
|
+
const items = [...(firstPage.items ?? [])]
|
|
16
|
+
const totalPages = firstPage.totalPages ?? 1
|
|
17
|
+
for (let page = 2; page <= totalPages; page += 1) {
|
|
18
|
+
const payload = await readPage(page)
|
|
19
|
+
items.push(...(payload.items ?? []))
|
|
20
|
+
}
|
|
21
|
+
return { items }
|
|
22
|
+
}
|