@open-mercato/core 0.10.1-develop.7883.1.44ac2125f5 → 0.10.1-develop.7894.1.2c464c73e3
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/audit_logs/api/audit-logs/actions/route.js +12 -26
- package/dist/modules/audit_logs/api/audit-logs/actions/route.js.map +2 -2
- package/dist/modules/audit_logs/services/actionLogService.js +10 -4
- package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js +3 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
- package/dist/modules/query_index/lib/coverage.js +36 -19
- package/dist/modules/query_index/lib/coverage.js.map +2 -2
- 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/audit_logs/api/audit-logs/actions/route.ts +18 -28
- package/src/modules/audit_logs/services/actionLogService.ts +20 -18
- package/src/modules/customers/components/detail/CompanyKpiBar.tsx +2 -1
- package/src/modules/query_index/lib/coverage.ts +54 -31
- 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
|
@@ -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 } 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
|
const TRACKER_STEP_KEYS = ["received", "review", "decision", "return", "resolved"];
|
|
34
36
|
const STATUS_STEP_INDEX = {
|
|
35
37
|
draft: 0,
|
|
@@ -236,25 +238,32 @@ function WarrantyClaimPortalDetailPage({ params }) {
|
|
|
236
238
|
const [actionSubmitting, setActionSubmitting] = React.useState(false);
|
|
237
239
|
const replacementInputRef = React.useRef(null);
|
|
238
240
|
const replacementTargetRef = React.useRef(null);
|
|
241
|
+
const claimRequestSequenceRef = React.useRef(0);
|
|
242
|
+
const eventsRequestSequenceRef = React.useRef(0);
|
|
239
243
|
React.useEffect(() => {
|
|
240
244
|
if (!loading && !user) {
|
|
241
245
|
replaceWithPageReload(`/${params.orgSlug}/portal/login`);
|
|
242
246
|
}
|
|
243
247
|
}, [loading, user, params.orgSlug]);
|
|
244
248
|
const refreshEvents = React.useCallback(async (claimId) => {
|
|
249
|
+
const requestSequence = ++eventsRequestSequenceRef.current;
|
|
245
250
|
const result = await apiCall(`/api/warranty_claims/portal/events?claimId=${encodeURIComponent(claimId)}`);
|
|
246
|
-
if (result.ok && result.result)
|
|
251
|
+
if (requestSequence === eventsRequestSequenceRef.current && result.ok && result.result) {
|
|
252
|
+
setEvents(result.result.items);
|
|
253
|
+
}
|
|
247
254
|
}, []);
|
|
248
255
|
const refreshAttachments = React.useCallback(async (claimId) => {
|
|
249
256
|
const result = await apiCall(`/api/warranty_claims/portal/attachments?claimId=${encodeURIComponent(claimId)}`);
|
|
250
257
|
if (result.ok && result.result) setAttachments(result.result.items);
|
|
251
258
|
}, []);
|
|
252
|
-
const loadClaim = React.useCallback(async () => {
|
|
253
|
-
|
|
259
|
+
const loadClaim = React.useCallback(async (showLoading = true) => {
|
|
260
|
+
const requestSequence = ++claimRequestSequenceRef.current;
|
|
261
|
+
if (showLoading) setIsLoading(true);
|
|
254
262
|
setError(null);
|
|
255
263
|
setNotFound(false);
|
|
256
264
|
try {
|
|
257
265
|
const detail = await apiCall(`/api/warranty_claims/portal/claims/${encodeURIComponent(params.id)}`);
|
|
266
|
+
if (requestSequence !== claimRequestSequenceRef.current) return;
|
|
258
267
|
if (detail.status === 404) {
|
|
259
268
|
setClaim(null);
|
|
260
269
|
setNotFound(true);
|
|
@@ -270,14 +279,31 @@ function WarrantyClaimPortalDetailPage({ params }) {
|
|
|
270
279
|
refreshAttachments(detail.result.item.id)
|
|
271
280
|
]);
|
|
272
281
|
} catch {
|
|
273
|
-
|
|
282
|
+
if (requestSequence === claimRequestSequenceRef.current) {
|
|
283
|
+
setError(t("warranty_claims.portal.detail.loadError"));
|
|
284
|
+
}
|
|
274
285
|
} finally {
|
|
275
|
-
setIsLoading(false);
|
|
286
|
+
if (requestSequence === claimRequestSequenceRef.current) setIsLoading(false);
|
|
276
287
|
}
|
|
277
288
|
}, [params.id, refreshAttachments, refreshEvents, t]);
|
|
278
289
|
React.useEffect(() => {
|
|
279
290
|
if (user) void loadClaim();
|
|
280
291
|
}, [loadClaim, user]);
|
|
292
|
+
usePortalAppEvent("warranty_claims.claim.portal_status_changed", (event) => {
|
|
293
|
+
if (!user || event.payload.claimId !== params.id) return;
|
|
294
|
+
void loadClaim(false);
|
|
295
|
+
});
|
|
296
|
+
usePortalAppEvent("warranty_claims.claim.comment_added", (event) => {
|
|
297
|
+
if (!user || event.payload.claimId !== params.id) return;
|
|
298
|
+
void refreshEvents(params.id).catch((error2) => {
|
|
299
|
+
getTelemetryRuntime()?.reportError(error2, {
|
|
300
|
+
module: "warranty_claims",
|
|
301
|
+
code: "warranty_claims.portal_timeline_refresh_failed",
|
|
302
|
+
attributes: { claimId: params.id }
|
|
303
|
+
});
|
|
304
|
+
setError(t("warranty_claims.portal.detail.loadError"));
|
|
305
|
+
});
|
|
306
|
+
});
|
|
281
307
|
React.useEffect(() => {
|
|
282
308
|
if (!user) return;
|
|
283
309
|
let cancelled = false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../../src/modules/warranty_claims/frontend/%5BorgSlug%5D/portal/claims/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { ArrowLeft, Check, CircleAlert, Info, Paperclip, RefreshCw, ShieldCheck, Trash2, TriangleAlert } from 'lucide-react'\nimport { replaceWithPageReload } from '@open-mercato/core/modules/portal/lib/navigation'\nimport { useLocale, useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { localizeDictionaryLabel } from '@open-mercato/core/modules/warranty_claims/lib/dictionaryLabels'\nimport { formatQuantity } from '@open-mercato/core/modules/warranty_claims/lib/quantity'\nimport {\n ATTACHMENT_ACCEPT_TYPES,\n validateAttachmentFile,\n} from '@open-mercato/core/modules/warranty_claims/lib/portalAttachmentValidation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { usePortalContext } from '@open-mercato/ui/portal/PortalContext'\nimport { PortalEmptyState } from '@open-mercato/ui/portal/components/PortalEmptyState'\n\ntype Props = { params: { orgSlug: string; id: string } }\n\ntype PortalClaimLine = {\n id: string\n lineNo: number\n sku: string | null\n productName: string | null\n serialNumber: string | null\n faultCode: string | null\n faultDescription: string | null\n qtyClaimed: string\n qtyApproved: string | null\n lineStatus: string\n disposition: string | null\n creditAmount: string | null\n}\n\ntype PortalClaim = {\n id: string\n claimNumber: string\n claimType: string\n status: string\n priority: string\n orderId: string | null\n orderNumber: string | null\n reasonCode: string | null\n rejectionReasonCode: string | null\n resolutionSummary: string | null\n submittedAt: string | null\n resolvedAt: string | null\n closedAt: string | null\n createdAt: string | null\n updatedAt: string | null\n lines: PortalClaimLine[]\n}\n\ntype PortalClaimEvent = {\n id: string\n kind: string\n body: string | null\n payload: Record<string, unknown> | null\n actorCustomerId: string | null\n createdAt: string | null\n}\n\ntype PortalAttachment = {\n id: string\n url: string\n downloadUrl: string\n fileName: string\n fileSize: number\n mimeType: string | null\n thumbnailUrl: string\n createdAt: string | null\n}\n\ntype ClaimResponse = { item: PortalClaim }\ntype EventResponse = { items: PortalClaimEvent[] }\ntype AttachmentResponse = { items: PortalAttachment[] }\ntype MutationOkResponse = { ok: boolean; error?: string }\ntype UploadResponse = MutationOkResponse & { item?: PortalAttachment }\ntype ClaimActionResponse = MutationOkResponse & { claimId?: string; status?: string }\ntype PortalOption = { value: string; label: string }\ntype PortalOptionsResponse = { ok: boolean; result?: { reasons: PortalOption[] } }\n\ntype PortalClaimAction = 'submit' | 'withdraw'\n\ntype TrackerStepState = 'complete' | 'current' | 'pending'\n\ntype TrackerStep = {\n id: string\n title: string\n description: string\n state: TrackerStepState\n dateLabel: string | null\n}\n\ntype BannerTone = 'info' | 'success' | 'warning' | 'error'\n\ntype ActivityEntry = {\n id: string\n title: string\n description: string\n createdAt: string | null\n href?: string\n attachment?: PortalAttachment\n}\n\nconst TRACKER_STEP_KEYS = ['received', 'review', 'decision', 'return', 'resolved'] as const\n\nconst STATUS_STEP_INDEX: Record<string, number> = {\n draft: 0,\n submitted: 1,\n in_review: 1,\n info_requested: 1,\n rejected: 2,\n cancelled: 2,\n approved: 3,\n awaiting_return: 3,\n received: 4,\n inspecting: 4,\n resolved: 5,\n closed: 5,\n}\n\nconst LINE_STATUS_DOT_CLASSES: Record<string, string> = {\n pending: 'bg-status-neutral-icon',\n approved: 'bg-status-success-icon',\n rejected: 'bg-status-error-icon',\n received: 'bg-status-info-icon',\n inspected: 'bg-status-info-icon',\n resolved: 'bg-status-success-icon',\n}\n\nconst BANNER_TONE_CLASSES: Record<BannerTone, { wrapper: string; text: string; icon: string }> = {\n info: {\n wrapper: 'bg-status-info-bg',\n text: 'text-status-info-text',\n icon: 'text-status-info-icon',\n },\n success: {\n wrapper: 'bg-status-success-bg',\n text: 'text-status-success-text',\n icon: 'text-status-success-icon',\n },\n warning: {\n wrapper: 'bg-status-warning-bg',\n text: 'text-status-warning-text',\n icon: 'text-status-warning-icon',\n },\n error: {\n wrapper: 'bg-status-error-bg',\n text: 'text-status-error-text',\n icon: 'text-status-error-icon',\n },\n}\n\nfunction bannerTone(status: string): BannerTone {\n if (status === 'rejected' || status === 'cancelled') return 'error'\n if (status === 'info_requested') return 'warning'\n if (status === 'approved' || status === 'resolved' || status === 'closed') return 'success'\n return 'info'\n}\n\nfunction bannerMessage(claim: PortalClaim, t: TranslateFn): string {\n switch (claim.status) {\n case 'draft':\n return t('warranty_claims.portal.tracker.banner.draft')\n case 'info_requested':\n return t('warranty_claims.portal.tracker.banner.infoRequested')\n case 'approved':\n return t('warranty_claims.portal.tracker.banner.approved')\n case 'awaiting_return':\n return t('warranty_claims.portal.tracker.banner.awaitingReturn')\n case 'received':\n case 'inspecting':\n return t('warranty_claims.portal.tracker.banner.goodsFlow')\n case 'resolved':\n case 'closed':\n return claim.resolutionSummary ?? t('warranty_claims.portal.tracker.banner.resolved')\n case 'rejected': {\n const reason = claim.rejectionReasonCode\n ? localizeDictionaryLabel(t, 'rejection', claim.rejectionReasonCode, claim.rejectionReasonCode)\n : null\n const base = t('warranty_claims.portal.detail.terminal.rejected')\n const parts = [base, reason, claim.resolutionSummary].filter((part): part is string => Boolean(part))\n return parts.join(' ')\n }\n case 'cancelled':\n return t('warranty_claims.portal.detail.terminal.cancelled')\n default:\n return t('warranty_claims.portal.tracker.banner.review')\n }\n}\n\nfunction formatShortDate(value: string | null, locale: string): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(locale || undefined, { month: 'short', day: 'numeric' })\n}\n\nfunction formatLongDate(value: string | null, locale: string): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(locale || undefined, { year: 'numeric', month: 'short', day: 'numeric' })\n}\n\nfunction buildTrackerSteps(claim: PortalClaim, t: TranslateFn, locale: string): TrackerStep[] {\n const currentIndex = STATUS_STEP_INDEX[claim.status] ?? 1\n const terminalComplete = claim.status === 'resolved' || claim.status === 'closed'\n return TRACKER_STEP_KEYS.map((key, index) => {\n const state: TrackerStepState = index < currentIndex || (terminalComplete && index === currentIndex)\n ? 'complete'\n : index === currentIndex\n ? 'current'\n : 'pending'\n let dateLabel: string | null = null\n if (index === 0 && state === 'complete') dateLabel = formatShortDate(claim.submittedAt, locale)\n if (index === TRACKER_STEP_KEYS.length - 1 && state === 'complete') {\n dateLabel = formatShortDate(\n claim.status === 'closed'\n ? claim.closedAt ?? claim.resolvedAt\n : claim.resolvedAt ?? claim.closedAt,\n locale,\n )\n }\n if (state === 'current') dateLabel = t('warranty_claims.portal.tracker.step.now')\n return {\n id: key,\n title: t(`warranty_claims.portal.tracker.step.${key}.title`),\n description: t(`warranty_claims.portal.tracker.step.${key}.description`),\n state,\n dateLabel,\n }\n })\n}\n\nfunction buildClaimTitle(claim: PortalClaim, reasonOptions: PortalOption[], t: TranslateFn): string {\n const typeLabel = t(`warranty_claims.claimType.${claim.claimType}`)\n if (claim.reasonCode) {\n const reasonCode = claim.reasonCode\n const dictionaryLabel = reasonOptions.find((option) => option.value === reasonCode)?.label ?? reasonCode\n const reasonLabel = localizeDictionaryLabel(t, 'reason', reasonCode, dictionaryLabel)\n return t('warranty_claims.portal.tracker.titleWithReason', { reason: reasonLabel, type: typeLabel })\n }\n return t('warranty_claims.portal.tracker.titleFallback', { type: typeLabel })\n}\n\nfunction buildClaimSubtitle(claim: PortalClaim, t: TranslateFn, locale: string): string {\n if (claim.status === 'draft') {\n const created = formatLongDate(claim.createdAt, locale)\n return created\n ? t('warranty_claims.portal.tracker.draftCreated', { date: created })\n : t('warranty_claims.portal.detail.notSubmitted')\n }\n const submitted = formatLongDate(claim.submittedAt, locale) ?? formatLongDate(claim.createdAt, locale)\n if (!submitted) return t('warranty_claims.portal.value.notAvailable')\n if (claim.orderNumber) {\n return t('warranty_claims.portal.tracker.submittedFromOrder', { date: submitted, order: claim.orderNumber })\n }\n return t('warranty_claims.portal.tracker.submittedOn', { date: submitted })\n}\n\nfunction formatEventBody(event: PortalClaimEvent, t: TranslateFn): string {\n if (event.body) return event.body\n const payload = event.payload\n const from = typeof payload?.from === 'string' ? payload.from : null\n const to = typeof payload?.to === 'string' ? payload.to : null\n if (from && to) {\n return t('warranty_claims.portal.event.transition', {\n from: t(`warranty_claims.status.${from}`),\n to: t(`warranty_claims.status.${to}`),\n })\n }\n return t('warranty_claims.portal.event.noDetails')\n}\n\nfunction eventTitle(event: PortalClaimEvent, t: TranslateFn): string {\n if (event.kind === 'comment') {\n return event.actorCustomerId\n ? t('warranty_claims.portal.tracker.event.youReplied')\n : t('warranty_claims.portal.tracker.event.teamReplied')\n }\n return t(`warranty_claims.portal.event.${event.kind}`)\n}\n\nfunction buildActivityEntries(\n events: PortalClaimEvent[],\n attachments: PortalAttachment[],\n t: TranslateFn,\n): ActivityEntry[] {\n const eventEntries: ActivityEntry[] = events.map((event) => ({\n id: `event-${event.id}`,\n title: eventTitle(event, t),\n description: formatEventBody(event, t),\n createdAt: event.createdAt,\n }))\n const attachmentEntries: ActivityEntry[] = attachments.map((attachment) => ({\n id: `attachment-${attachment.id}`,\n title: t('warranty_claims.portal.tracker.event.attachmentAdded'),\n description: attachment.fileName,\n createdAt: attachment.createdAt,\n href: attachment.downloadUrl,\n attachment,\n }))\n return [...eventEntries, ...attachmentEntries].sort((left, right) => {\n const leftTime = left.createdAt ? new Date(left.createdAt).getTime() : 0\n const rightTime = right.createdAt ? new Date(right.createdAt).getTime() : 0\n return rightTime - leftTime\n })\n}\n\nconst DETAIL_MUTATION_CONTEXT_ID = 'warranty_claims.portal.claim.detail'\n\nconst SECTION_CLASS = 'flex w-full flex-col gap-3 border-t border-border px-6 pb-6 pt-5'\nconst SECTION_HEADING_CLASS = 'text-sm font-semibold text-foreground'\nconst DARK_BUTTON_CLASS = 'rounded-md px-3 py-2 text-sm font-medium'\n\nexport default function WarrantyClaimPortalDetailPage({ params }: Props) {\n const t = useT()\n const locale = useLocale()\n const { auth } = usePortalContext()\n const { user, loading } = auth\n const guardedMutation = useGuardedMutation<Record<string, unknown>>({\n contextId: DETAIL_MUTATION_CONTEXT_ID,\n blockedMessage: t('warranty_claims.portal.detail.blocked'),\n })\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [claim, setClaim] = React.useState<PortalClaim | null>(null)\n const [events, setEvents] = React.useState<PortalClaimEvent[]>([])\n const [attachments, setAttachments] = React.useState<PortalAttachment[]>([])\n const [reasonOptions, setReasonOptions] = React.useState<PortalOption[]>([])\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [notFound, setNotFound] = React.useState(false)\n const [comment, setComment] = React.useState('')\n const [commentSubmitting, setCommentSubmitting] = React.useState(false)\n const [fileInputKey, setFileInputKey] = React.useState(0)\n const [uploading, setUploading] = React.useState(false)\n const [attachmentMutationId, setAttachmentMutationId] = React.useState<string | null>(null)\n const [claimAction, setClaimAction] = React.useState<PortalClaimAction | null>(null)\n const [actionSubmitting, setActionSubmitting] = React.useState(false)\n const replacementInputRef = React.useRef<HTMLInputElement | null>(null)\n const replacementTargetRef = React.useRef<PortalAttachment | null>(null)\n\n // Leaving an authenticated page for the login page crosses the public/authenticated\n // boundary, so it must be a full page load \u2014 see `replaceWithPageReload`.\n React.useEffect(() => {\n if (!loading && !user) {\n replaceWithPageReload(`/${params.orgSlug}/portal/login`)\n }\n }, [loading, user, params.orgSlug])\n\n const refreshEvents = React.useCallback(async (claimId: string) => {\n const result = await apiCall<EventResponse>(`/api/warranty_claims/portal/events?claimId=${encodeURIComponent(claimId)}`)\n if (result.ok && result.result) setEvents(result.result.items)\n }, [])\n\n const refreshAttachments = React.useCallback(async (claimId: string) => {\n const result = await apiCall<AttachmentResponse>(`/api/warranty_claims/portal/attachments?claimId=${encodeURIComponent(claimId)}`)\n if (result.ok && result.result) setAttachments(result.result.items)\n }, [])\n\n const loadClaim = React.useCallback(async () => {\n setIsLoading(true)\n setError(null)\n setNotFound(false)\n try {\n const detail = await apiCall<ClaimResponse>(`/api/warranty_claims/portal/claims/${encodeURIComponent(params.id)}`)\n if (detail.status === 404) {\n setClaim(null)\n setNotFound(true)\n return\n }\n if (!detail.ok || !detail.result) {\n setError(t('warranty_claims.portal.detail.loadError'))\n return\n }\n setClaim(detail.result.item)\n await Promise.all([\n refreshEvents(detail.result.item.id),\n refreshAttachments(detail.result.item.id),\n ])\n } catch {\n setError(t('warranty_claims.portal.detail.loadError'))\n } finally {\n setIsLoading(false)\n }\n }, [params.id, refreshAttachments, refreshEvents, t])\n\n React.useEffect(() => {\n if (user) void loadClaim()\n }, [loadClaim, user])\n\n React.useEffect(() => {\n if (!user) return\n let cancelled = false\n apiCall<PortalOptionsResponse>('/api/warranty_claims/portal/options')\n .then((result) => {\n if (cancelled) return\n setReasonOptions(result.result?.ok ? result.result.result?.reasons ?? [] : [])\n })\n .catch(() => {})\n return () => {\n cancelled = true\n }\n }, [user])\n\n const submitComment = React.useCallback(async () => {\n if (!claim || commentSubmitting) return\n const body = comment.trim()\n if (!body) return\n setCommentSubmitting(true)\n setError(null)\n try {\n const result = await guardedMutation.runMutation({\n operation: () => apiCall<MutationOkResponse>('/api/warranty_claims/portal/events', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({ claimId: claim.id, body }),\n }),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'warranty_claims.claim_event',\n operation: 'portal_comment',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'warranty_claims.claim_event',\n resourceId: claim.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: { claimId: claim.id, body },\n })\n if (!result.ok || !result.result?.ok) {\n setError(t('warranty_claims.portal.detail.commentError'))\n return\n }\n setComment('')\n flash(t('warranty_claims.portal.detail.commentSuccess'), 'success')\n await refreshEvents(claim.id)\n } catch {\n setError(t('warranty_claims.portal.detail.commentError'))\n } finally {\n setCommentSubmitting(false)\n }\n }, [claim, comment, commentSubmitting, guardedMutation, refreshEvents, t])\n\n const uploadAttachment = React.useCallback(async (file: File, replacementTarget?: PortalAttachment) => {\n if (!claim || uploading) return\n const validationError = await validateAttachmentFile(file, t)\n if (validationError) {\n setError(validationError)\n flash(validationError, 'error')\n return\n }\n setUploading(true)\n setAttachmentMutationId(replacementTarget?.id ?? 'new')\n setError(null)\n try {\n const form = new FormData()\n form.set('claimId', claim.id)\n form.set('file', file)\n if (replacementTarget) form.set('attachmentId', replacementTarget.id)\n const result = await guardedMutation.runMutation({\n operation: () => apiCall<UploadResponse>('/api/warranty_claims/portal/attachments', {\n method: replacementTarget ? 'PUT' : 'POST',\n credentials: 'include',\n body: form,\n }),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'attachments.attachment',\n operation: replacementTarget ? 'portal_attachment_replace' : 'portal_attachment_upload',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'attachments.attachment',\n resourceId: replacementTarget?.id ?? claim.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: {\n claimId: claim.id,\n attachmentId: replacementTarget?.id,\n fileName: file.name,\n fileSize: file.size,\n },\n })\n if (!result.ok || !result.result?.ok) {\n setError(replacementTarget\n ? t('warranty_claims.portal.detail.attachmentReplaceError', 'Attachment replacement failed.')\n : t('warranty_claims.portal.detail.attachmentError'))\n return\n }\n flash(\n replacementTarget\n ? t('warranty_claims.portal.detail.attachmentReplaceSuccess', 'Attachment replaced.')\n : t('warranty_claims.portal.detail.attachmentSuccess'),\n 'success',\n )\n await refreshAttachments(claim.id)\n } catch {\n setError(replacementTarget\n ? t('warranty_claims.portal.detail.attachmentReplaceError', 'Attachment replacement failed.')\n : t('warranty_claims.portal.detail.attachmentError'))\n } finally {\n setUploading(false)\n setAttachmentMutationId(null)\n setFileInputKey((current) => current + 1)\n }\n }, [claim, guardedMutation, refreshAttachments, t, uploading])\n\n const deleteAttachment = React.useCallback(async (attachment: PortalAttachment) => {\n if (!claim || uploading) return\n const confirmed = await confirm({\n title: t('warranty_claims.portal.detail.attachmentDelete', 'Delete attachment'),\n text: t(\n 'warranty_claims.portal.detail.attachmentDeleteConfirm',\n 'Delete \"{name}\"? This action cannot be undone.',\n { name: attachment.fileName },\n ),\n confirmText: t('warranty_claims.portal.detail.attachmentDelete', 'Delete attachment'),\n cancelText: t('warranty_claims.portal.detail.dialogCancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setUploading(true)\n setAttachmentMutationId(attachment.id)\n setError(null)\n try {\n const result = await guardedMutation.runMutation({\n operation: () => apiCall<MutationOkResponse>(\n `/api/warranty_claims/portal/attachments?attachmentId=${encodeURIComponent(attachment.id)}`,\n { method: 'DELETE', credentials: 'include' },\n ),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'attachments.attachment',\n operation: 'portal_attachment_delete',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'attachments.attachment',\n resourceId: attachment.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: { claimId: claim.id, attachmentId: attachment.id },\n })\n if (!result.ok || !result.result?.ok) {\n setError(t('warranty_claims.portal.detail.attachmentDeleteError', 'Attachment deletion failed.'))\n return\n }\n flash(t('warranty_claims.portal.detail.attachmentDeleteSuccess', 'Attachment deleted.'), 'success')\n await refreshAttachments(claim.id)\n } catch {\n setError(t('warranty_claims.portal.detail.attachmentDeleteError', 'Attachment deletion failed.'))\n } finally {\n setUploading(false)\n setAttachmentMutationId(null)\n }\n }, [claim, confirm, guardedMutation, refreshAttachments, t, uploading])\n\n const requestAttachmentReplacement = React.useCallback((attachment: PortalAttachment) => {\n if (uploading) return\n replacementTargetRef.current = attachment\n replacementInputRef.current?.click()\n }, [uploading])\n\n const runClaimAction = React.useCallback(async (action: PortalClaimAction) => {\n if (!claim || actionSubmitting) return\n setActionSubmitting(true)\n setError(null)\n try {\n const endpoint = `/api/warranty_claims/portal/claims/${encodeURIComponent(claim.id)}/${action}`\n const result = await guardedMutation.runMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(claim.updatedAt),\n () => apiCall<ClaimActionResponse>(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({}),\n }),\n ),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'warranty_claims.claim',\n operation: action === 'submit' ? 'portal_submit' : 'portal_withdraw',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'warranty_claims.claim',\n resourceId: claim.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: { claimId: claim.id, action },\n })\n if (!result.ok || !result.result?.ok) {\n const message = result.status === 409\n ? t('warranty_claims.portal.detail.actionConflict', 'This claim changed in the meantime. It has been reloaded \u2014 please try again.')\n : action === 'submit'\n ? t('warranty_claims.portal.detail.submitError', 'The claim could not be submitted.')\n : t('warranty_claims.portal.detail.withdrawError', 'The claim could not be withdrawn.')\n setError(message)\n flash(message, 'error')\n if (result.status === 409) {\n setClaimAction(null)\n await loadClaim()\n }\n return\n }\n setClaimAction(null)\n flash(\n action === 'submit'\n ? t('warranty_claims.portal.detail.submitSuccess', 'Your claim has been submitted.')\n : t('warranty_claims.portal.detail.withdrawSuccess', 'Your claim has been withdrawn.'),\n 'success',\n )\n await loadClaim()\n } catch {\n const message = action === 'submit'\n ? t('warranty_claims.portal.detail.submitError', 'The claim could not be submitted.')\n : t('warranty_claims.portal.detail.withdrawError', 'The claim could not be withdrawn.')\n setError(message)\n flash(message, 'error')\n } finally {\n setActionSubmitting(false)\n }\n }, [actionSubmitting, claim, guardedMutation, loadClaim, t])\n\n const handleSelectedFileChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0] ?? null\n if (!file) return\n void uploadAttachment(file)\n }, [uploadAttachment])\n\n const handleReplacementFileChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const replacementTarget = replacementTargetRef.current\n replacementTargetRef.current = null\n const file = event.target.files?.[0] ?? null\n event.target.value = ''\n if (!replacementTarget || !file) return\n void uploadAttachment(file, replacementTarget)\n }, [uploadAttachment])\n\n const handleCommentSubmit = React.useCallback((event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n void submitComment()\n }, [submitComment])\n\n const handleCommentKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void submitComment()\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setComment('')\n }\n }, [submitComment])\n\n if (loading) {\n return <div className=\"flex items-center justify-center py-20\"><Spinner /></div>\n }\n\n if (!user) return null\n\n if (isLoading) {\n return <LoadingMessage label={t('warranty_claims.portal.detail.loading')} />\n }\n\n if (notFound) {\n return (\n <PortalEmptyState\n icon={<ShieldCheck className=\"size-5\" />}\n title={t('warranty_claims.portal.detail.notFoundTitle')}\n description={t('warranty_claims.portal.detail.notFoundDescription')}\n action={\n <Button asChild>\n <Link href={`/${params.orgSlug}/portal/claims`}>\n <ArrowLeft className=\"size-4\" aria-hidden=\"true\" />\n {t('warranty_claims.portal.detail.back')}\n </Link>\n </Button>\n }\n />\n )\n }\n\n if (!claim) {\n return <ErrorMessage label={error ?? t('warranty_claims.portal.detail.loadError')} />\n }\n\n const trackerSteps = buildTrackerSteps(claim, t, locale)\n const isDraft = claim.status === 'draft'\n const canWithdraw = claim.status === 'draft' || claim.status === 'submitted'\n const tone = bannerTone(claim.status)\n const toneClasses = BANNER_TONE_CLASSES[tone]\n const BannerIcon = tone === 'error' ? CircleAlert : tone === 'warning' ? TriangleAlert : Info\n const activityEntries = buildActivityEntries(events, attachments, t)\n\n return (\n <div className=\"mx-auto flex w-full max-w-3xl flex-col gap-4\">\n <Link\n href={`/${params.orgSlug}/portal/claims`}\n className=\"text-sm font-medium text-muted-foreground transition-colors hover:text-foreground\"\n >\n {'\u2190'} {t('warranty_claims.portal.listTitle')}\n </Link>\n\n {error ? <ErrorMessage label={error} /> : null}\n\n <div className=\"w-full overflow-hidden rounded-lg border border-border bg-card\">\n <div className=\"flex w-full flex-col gap-3 px-6 pb-5 pt-6\">\n <div className=\"flex w-full items-center gap-2\">\n <div className=\"flex items-center gap-2 rounded-sm border border-border bg-background px-3 py-1\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t(`warranty_claims.claimType.${claim.claimType}`)}\n </span>\n <span className=\"text-sm font-semibold text-foreground\">\n {claim.claimNumber}\n </span>\n </div>\n <div className=\"min-w-px flex-1\" />\n {isDraft ? (\n <Button\n type=\"button\"\n size=\"sm\"\n className={DARK_BUTTON_CLASS}\n onClick={() => setClaimAction('submit')}\n disabled={actionSubmitting}\n >\n {t('warranty_claims.portal.submit')}\n </Button>\n ) : null}\n {canWithdraw ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"destructive\"\n className=\"rounded-md px-3 py-2 text-sm font-medium\"\n onClick={() => setClaimAction('withdraw')}\n disabled={actionSubmitting}\n >\n {t('warranty_claims.portal.detail.withdraw', 'Withdraw claim')}\n </Button>\n ) : null}\n </div>\n <h1 className=\"text-xl font-bold text-foreground\">\n {buildClaimTitle(claim, reasonOptions, t)}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {buildClaimSubtitle(claim, t, locale)}\n </p>\n </div>\n\n <div className=\"w-full px-6 pb-6\">\n <div className={`flex w-full items-start gap-3 rounded-lg px-4 py-3 ${toneClasses.wrapper}`}>\n <BannerIcon className={`mt-px size-4 shrink-0 ${toneClasses.icon}`} aria-hidden=\"true\" />\n <p className={`min-w-px flex-1 text-sm ${toneClasses.text}`}>\n {bannerMessage(claim, t)}\n </p>\n </div>\n </div>\n\n <div className=\"flex w-full flex-col px-6 pb-6\">\n {trackerSteps.map((step, index) => (\n <div key={step.id} className=\"flex w-full items-stretch gap-4\">\n <div className=\"flex flex-col items-center\">\n {step.state === 'complete' ? (\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-status-success-icon\">\n <Check className=\"size-3 text-status-success-bg\" strokeWidth={2.5} aria-hidden=\"true\" />\n </span>\n ) : step.state === 'current' ? (\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-primary text-xs font-semibold text-primary-foreground\">\n {index + 1}\n </span>\n ) : (\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full border border-border text-xs font-semibold text-muted-foreground\">\n {index + 1}\n </span>\n )}\n {index < trackerSteps.length - 1 ? (\n <span\n className={`min-h-2 w-px flex-1 ${step.state === 'complete' ? 'bg-status-success-icon' : 'bg-border'}`}\n />\n ) : null}\n </div>\n <div className=\"flex min-w-px flex-1 flex-col gap-1 pb-4\">\n <div className=\"flex w-full items-center gap-2\">\n <p\n className={\n step.state === 'pending'\n ? 'text-sm font-medium text-muted-foreground'\n : 'text-sm font-semibold text-foreground'\n }\n >\n {step.title}\n </p>\n <div className=\"min-w-px flex-1\" />\n {step.dateLabel ? (\n <p className=\"text-xs text-muted-foreground\">{step.dateLabel}</p>\n ) : null}\n </div>\n <p className=\"w-full text-xs text-muted-foreground\">{step.description}</p>\n </div>\n </div>\n ))}\n </div>\n\n <div className={SECTION_CLASS}>\n <p className={SECTION_HEADING_CLASS}>{t('warranty_claims.portal.tracker.items')}</p>\n {claim.lines.length > 0 ? (\n claim.lines.map((line) => (\n <div\n key={line.id}\n className=\"flex w-full items-center gap-3 rounded-lg border border-border px-4 py-3\"\n >\n <div className=\"flex min-w-px flex-1 flex-col gap-1\">\n <p className=\"text-sm font-medium text-foreground\">\n {line.productName ?? line.sku ?? t('warranty_claims.portal.value.unnamedLine')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {line.sku\n ? t('warranty_claims.portal.tracker.itemMeta', {\n sku: line.sku,\n qty: formatQuantity(line.qtyClaimed, t('warranty_claims.portal.value.notAvailable')),\n })\n : t('warranty_claims.portal.tracker.itemQty', {\n qty: formatQuantity(line.qtyClaimed, t('warranty_claims.portal.value.notAvailable')),\n })}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2 rounded-sm border border-border bg-background px-2 py-1\">\n <span\n className={`size-2 shrink-0 rounded-full ${LINE_STATUS_DOT_CLASSES[line.lineStatus] ?? 'bg-status-neutral-icon'}`}\n aria-hidden=\"true\"\n />\n <span className=\"text-xs font-medium text-foreground\">\n {t(`warranty_claims.lineStatus.${line.lineStatus}`)}\n </span>\n </div>\n </div>\n ))\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('warranty_claims.portal.detail.linesEmpty.description')}\n </p>\n )}\n </div>\n\n <div className={SECTION_CLASS}>\n <p className={SECTION_HEADING_CLASS}>{t('warranty_claims.portal.tracker.activity')}</p>\n {activityEntries.length > 0 ? (\n activityEntries.map((entry) => (\n <div key={entry.id} className=\"flex w-full items-start gap-3\">\n <span className=\"mt-1 size-2 shrink-0 rounded-full bg-status-neutral-icon\" aria-hidden=\"true\" />\n <div className=\"flex min-w-px flex-1 flex-col gap-px\">\n <div className=\"flex w-full items-start gap-2\">\n <p className=\"text-sm font-medium text-foreground\">{entry.title}</p>\n <div className=\"min-w-px flex-1\" />\n {entry.createdAt ? (\n <p className=\"text-xs text-muted-foreground\">{formatShortDate(entry.createdAt, locale)}</p>\n ) : null}\n </div>\n {entry.href ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n <a\n href={entry.href}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"min-w-0 whitespace-pre-wrap text-xs text-muted-foreground underline-offset-2 hover:underline\"\n >\n {entry.description}\n </a>\n {entry.attachment ? (\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"px-2 text-xs\"\n aria-label={t(\n 'warranty_claims.portal.detail.attachmentReplaceNamed',\n 'Replace {name}',\n { name: entry.attachment.fileName },\n )}\n onClick={() => requestAttachmentReplacement(entry.attachment!)}\n disabled={uploading}\n >\n {attachmentMutationId === entry.attachment.id ? (\n <Spinner className=\"size-3\" />\n ) : (\n <RefreshCw className=\"size-3\" aria-hidden=\"true\" />\n )}\n {t('warranty_claims.portal.detail.attachmentReplace', 'Replace')}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"px-2 text-xs text-status-error-text\"\n aria-label={t(\n 'warranty_claims.portal.detail.attachmentDeleteNamed',\n 'Delete {name}',\n { name: entry.attachment.fileName },\n )}\n onClick={() => { void deleteAttachment(entry.attachment!) }}\n disabled={uploading}\n >\n <Trash2 className=\"size-3\" aria-hidden=\"true\" />\n {t('warranty_claims.portal.detail.attachmentDelete', 'Delete attachment')}\n </Button>\n </div>\n ) : null}\n </div>\n ) : (\n <p className=\"whitespace-pre-wrap text-xs text-muted-foreground\">{entry.description}</p>\n )}\n </div>\n </div>\n ))\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('warranty_claims.portal.detail.timelineEmpty.description')}\n </p>\n )}\n <input\n ref={replacementInputRef}\n type=\"file\"\n accept={ATTACHMENT_ACCEPT_TYPES}\n onChange={handleReplacementFileChange}\n disabled={uploading}\n className=\"sr-only\"\n aria-label={t('warranty_claims.portal.detail.attachmentChooseReplacement', 'Choose replacement file')}\n />\n </div>\n\n <div className={SECTION_CLASS}>\n <p className={SECTION_HEADING_CLASS}>{t('warranty_claims.portal.tracker.message')}</p>\n <form id=\"warranty-claim-comment-box\" className=\"flex w-full flex-col gap-3\" onSubmit={handleCommentSubmit}>\n <Textarea\n value={comment}\n onChange={(event) => setComment(event.target.value)}\n onKeyDown={handleCommentKeyDown}\n placeholder={t('warranty_claims.portal.tracker.messagePlaceholder')}\n disabled={commentSubmitting}\n maxLength={8000}\n className=\"h-40 resize-none rounded-lg border-input bg-background px-3 py-2 text-sm shadow-xs placeholder:text-muted-foreground\"\n />\n <div className=\"flex w-full items-center gap-2\">\n <label className={`flex items-center gap-2 ${uploading ? 'cursor-default opacity-70' : 'cursor-pointer'}`}>\n {uploading ? (\n <Spinner className=\"size-4\" />\n ) : (\n <Paperclip className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n )}\n <span className=\"text-xs font-medium text-muted-foreground transition-colors hover:text-foreground\">\n {uploading\n ? t('warranty_claims.portal.detail.uploading')\n : t('warranty_claims.portal.tracker.attachFiles')}\n </span>\n <input\n key={fileInputKey}\n type=\"file\"\n accept={ATTACHMENT_ACCEPT_TYPES}\n onChange={handleSelectedFileChange}\n disabled={uploading}\n className=\"sr-only\"\n />\n </label>\n <div className=\"min-w-px flex-1\" />\n <Button type=\"submit\" size=\"sm\" className={DARK_BUTTON_CLASS} disabled={commentSubmitting || !comment.trim()}>\n {commentSubmitting\n ? t('warranty_claims.portal.tracker.sending')\n : t('warranty_claims.portal.tracker.send')}\n </Button>\n </div>\n </form>\n </div>\n </div>\n\n <Dialog\n open={claimAction !== null}\n onOpenChange={(open) => {\n if (!open && !actionSubmitting) setClaimAction(null)\n }}\n >\n <DialogContent\n className=\"max-w-lg\"\n onKeyDown={(event) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (claimAction && !actionSubmitting) void runClaimAction(claimAction)\n }\n if (event.key === 'Escape' && !actionSubmitting) {\n setClaimAction(null)\n }\n }}\n >\n <DialogHeader>\n <DialogTitle>\n {claimAction === 'withdraw'\n ? t('warranty_claims.portal.detail.withdrawDialog.title', 'Withdraw this claim?')\n : t('warranty_claims.portal.detail.submitDialog.title', 'Submit this claim?')}\n </DialogTitle>\n <DialogDescription>\n {claimAction === 'withdraw'\n ? t('warranty_claims.portal.detail.withdrawDialog.description', 'The claim will be cancelled and cannot be reopened. This cannot be undone.')\n : t('warranty_claims.portal.detail.submitDialog.description', 'Your claim will be sent to our support team for review. You can still add comments and photos afterwards.')}\n </DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setClaimAction(null)}\n disabled={actionSubmitting}\n >\n {t('warranty_claims.portal.detail.dialogCancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n variant={claimAction === 'withdraw' ? 'destructive' : 'default'}\n onClick={() => {\n if (claimAction) void runClaimAction(claimAction)\n }}\n disabled={actionSubmitting}\n >\n {claimAction === 'withdraw'\n ? (actionSubmitting\n ? t('warranty_claims.portal.detail.withdrawing', 'Withdrawing...')\n : t('warranty_claims.portal.detail.withdrawConfirm', 'Withdraw claim'))\n : (actionSubmitting\n ? t('warranty_claims.portal.detail.submitting', 'Submitting...')\n : t('warranty_claims.portal.detail.submitConfirm', 'Submit claim'))}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </div>\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 { ArrowLeft, Check, CircleAlert, Info, Paperclip, RefreshCw, ShieldCheck, Trash2, TriangleAlert } from 'lucide-react'\nimport { replaceWithPageReload } from '@open-mercato/core/modules/portal/lib/navigation'\nimport { useLocale, useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { getTelemetryRuntime } from '@open-mercato/shared/lib/telemetry/runtime'\nimport { localizeDictionaryLabel } from '@open-mercato/core/modules/warranty_claims/lib/dictionaryLabels'\nimport { formatQuantity } from '@open-mercato/core/modules/warranty_claims/lib/quantity'\nimport {\n ATTACHMENT_ACCEPT_TYPES,\n validateAttachmentFile,\n} from '@open-mercato/core/modules/warranty_claims/lib/portalAttachmentValidation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorMessage, LoadingMessage } from '@open-mercato/ui/backend/detail'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { usePortalContext } from '@open-mercato/ui/portal/PortalContext'\nimport { PortalEmptyState } from '@open-mercato/ui/portal/components/PortalEmptyState'\nimport { usePortalAppEvent } from '@open-mercato/ui/portal/hooks/usePortalAppEvent'\n\ntype Props = { params: { orgSlug: string; id: string } }\n\ntype PortalClaimLine = {\n id: string\n lineNo: number\n sku: string | null\n productName: string | null\n serialNumber: string | null\n faultCode: string | null\n faultDescription: string | null\n qtyClaimed: string\n qtyApproved: string | null\n lineStatus: string\n disposition: string | null\n creditAmount: string | null\n}\n\ntype PortalClaim = {\n id: string\n claimNumber: string\n claimType: string\n status: string\n priority: string\n orderId: string | null\n orderNumber: string | null\n reasonCode: string | null\n rejectionReasonCode: string | null\n resolutionSummary: string | null\n submittedAt: string | null\n resolvedAt: string | null\n closedAt: string | null\n createdAt: string | null\n updatedAt: string | null\n lines: PortalClaimLine[]\n}\n\ntype PortalClaimEvent = {\n id: string\n kind: string\n body: string | null\n payload: Record<string, unknown> | null\n actorCustomerId: string | null\n createdAt: string | null\n}\n\ntype PortalAttachment = {\n id: string\n url: string\n downloadUrl: string\n fileName: string\n fileSize: number\n mimeType: string | null\n thumbnailUrl: string\n createdAt: string | null\n}\n\ntype ClaimResponse = { item: PortalClaim }\ntype EventResponse = { items: PortalClaimEvent[] }\ntype AttachmentResponse = { items: PortalAttachment[] }\ntype MutationOkResponse = { ok: boolean; error?: string }\ntype UploadResponse = MutationOkResponse & { item?: PortalAttachment }\ntype ClaimActionResponse = MutationOkResponse & { claimId?: string; status?: string }\ntype PortalOption = { value: string; label: string }\ntype PortalOptionsResponse = { ok: boolean; result?: { reasons: PortalOption[] } }\n\ntype PortalClaimAction = 'submit' | 'withdraw'\n\ntype TrackerStepState = 'complete' | 'current' | 'pending'\n\ntype TrackerStep = {\n id: string\n title: string\n description: string\n state: TrackerStepState\n dateLabel: string | null\n}\n\ntype BannerTone = 'info' | 'success' | 'warning' | 'error'\n\ntype ActivityEntry = {\n id: string\n title: string\n description: string\n createdAt: string | null\n href?: string\n attachment?: PortalAttachment\n}\n\nconst TRACKER_STEP_KEYS = ['received', 'review', 'decision', 'return', 'resolved'] as const\n\nconst STATUS_STEP_INDEX: Record<string, number> = {\n draft: 0,\n submitted: 1,\n in_review: 1,\n info_requested: 1,\n rejected: 2,\n cancelled: 2,\n approved: 3,\n awaiting_return: 3,\n received: 4,\n inspecting: 4,\n resolved: 5,\n closed: 5,\n}\n\nconst LINE_STATUS_DOT_CLASSES: Record<string, string> = {\n pending: 'bg-status-neutral-icon',\n approved: 'bg-status-success-icon',\n rejected: 'bg-status-error-icon',\n received: 'bg-status-info-icon',\n inspected: 'bg-status-info-icon',\n resolved: 'bg-status-success-icon',\n}\n\nconst BANNER_TONE_CLASSES: Record<BannerTone, { wrapper: string; text: string; icon: string }> = {\n info: {\n wrapper: 'bg-status-info-bg',\n text: 'text-status-info-text',\n icon: 'text-status-info-icon',\n },\n success: {\n wrapper: 'bg-status-success-bg',\n text: 'text-status-success-text',\n icon: 'text-status-success-icon',\n },\n warning: {\n wrapper: 'bg-status-warning-bg',\n text: 'text-status-warning-text',\n icon: 'text-status-warning-icon',\n },\n error: {\n wrapper: 'bg-status-error-bg',\n text: 'text-status-error-text',\n icon: 'text-status-error-icon',\n },\n}\n\nfunction bannerTone(status: string): BannerTone {\n if (status === 'rejected' || status === 'cancelled') return 'error'\n if (status === 'info_requested') return 'warning'\n if (status === 'approved' || status === 'resolved' || status === 'closed') return 'success'\n return 'info'\n}\n\nfunction bannerMessage(claim: PortalClaim, t: TranslateFn): string {\n switch (claim.status) {\n case 'draft':\n return t('warranty_claims.portal.tracker.banner.draft')\n case 'info_requested':\n return t('warranty_claims.portal.tracker.banner.infoRequested')\n case 'approved':\n return t('warranty_claims.portal.tracker.banner.approved')\n case 'awaiting_return':\n return t('warranty_claims.portal.tracker.banner.awaitingReturn')\n case 'received':\n case 'inspecting':\n return t('warranty_claims.portal.tracker.banner.goodsFlow')\n case 'resolved':\n case 'closed':\n return claim.resolutionSummary ?? t('warranty_claims.portal.tracker.banner.resolved')\n case 'rejected': {\n const reason = claim.rejectionReasonCode\n ? localizeDictionaryLabel(t, 'rejection', claim.rejectionReasonCode, claim.rejectionReasonCode)\n : null\n const base = t('warranty_claims.portal.detail.terminal.rejected')\n const parts = [base, reason, claim.resolutionSummary].filter((part): part is string => Boolean(part))\n return parts.join(' ')\n }\n case 'cancelled':\n return t('warranty_claims.portal.detail.terminal.cancelled')\n default:\n return t('warranty_claims.portal.tracker.banner.review')\n }\n}\n\nfunction formatShortDate(value: string | null, locale: string): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(locale || undefined, { month: 'short', day: 'numeric' })\n}\n\nfunction formatLongDate(value: string | null, locale: string): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n return date.toLocaleDateString(locale || undefined, { year: 'numeric', month: 'short', day: 'numeric' })\n}\n\nfunction buildTrackerSteps(claim: PortalClaim, t: TranslateFn, locale: string): TrackerStep[] {\n const currentIndex = STATUS_STEP_INDEX[claim.status] ?? 1\n const terminalComplete = claim.status === 'resolved' || claim.status === 'closed'\n return TRACKER_STEP_KEYS.map((key, index) => {\n const state: TrackerStepState = index < currentIndex || (terminalComplete && index === currentIndex)\n ? 'complete'\n : index === currentIndex\n ? 'current'\n : 'pending'\n let dateLabel: string | null = null\n if (index === 0 && state === 'complete') dateLabel = formatShortDate(claim.submittedAt, locale)\n if (index === TRACKER_STEP_KEYS.length - 1 && state === 'complete') {\n dateLabel = formatShortDate(\n claim.status === 'closed'\n ? claim.closedAt ?? claim.resolvedAt\n : claim.resolvedAt ?? claim.closedAt,\n locale,\n )\n }\n if (state === 'current') dateLabel = t('warranty_claims.portal.tracker.step.now')\n return {\n id: key,\n title: t(`warranty_claims.portal.tracker.step.${key}.title`),\n description: t(`warranty_claims.portal.tracker.step.${key}.description`),\n state,\n dateLabel,\n }\n })\n}\n\nfunction buildClaimTitle(claim: PortalClaim, reasonOptions: PortalOption[], t: TranslateFn): string {\n const typeLabel = t(`warranty_claims.claimType.${claim.claimType}`)\n if (claim.reasonCode) {\n const reasonCode = claim.reasonCode\n const dictionaryLabel = reasonOptions.find((option) => option.value === reasonCode)?.label ?? reasonCode\n const reasonLabel = localizeDictionaryLabel(t, 'reason', reasonCode, dictionaryLabel)\n return t('warranty_claims.portal.tracker.titleWithReason', { reason: reasonLabel, type: typeLabel })\n }\n return t('warranty_claims.portal.tracker.titleFallback', { type: typeLabel })\n}\n\nfunction buildClaimSubtitle(claim: PortalClaim, t: TranslateFn, locale: string): string {\n if (claim.status === 'draft') {\n const created = formatLongDate(claim.createdAt, locale)\n return created\n ? t('warranty_claims.portal.tracker.draftCreated', { date: created })\n : t('warranty_claims.portal.detail.notSubmitted')\n }\n const submitted = formatLongDate(claim.submittedAt, locale) ?? formatLongDate(claim.createdAt, locale)\n if (!submitted) return t('warranty_claims.portal.value.notAvailable')\n if (claim.orderNumber) {\n return t('warranty_claims.portal.tracker.submittedFromOrder', { date: submitted, order: claim.orderNumber })\n }\n return t('warranty_claims.portal.tracker.submittedOn', { date: submitted })\n}\n\nfunction formatEventBody(event: PortalClaimEvent, t: TranslateFn): string {\n if (event.body) return event.body\n const payload = event.payload\n const from = typeof payload?.from === 'string' ? payload.from : null\n const to = typeof payload?.to === 'string' ? payload.to : null\n if (from && to) {\n return t('warranty_claims.portal.event.transition', {\n from: t(`warranty_claims.status.${from}`),\n to: t(`warranty_claims.status.${to}`),\n })\n }\n return t('warranty_claims.portal.event.noDetails')\n}\n\nfunction eventTitle(event: PortalClaimEvent, t: TranslateFn): string {\n if (event.kind === 'comment') {\n return event.actorCustomerId\n ? t('warranty_claims.portal.tracker.event.youReplied')\n : t('warranty_claims.portal.tracker.event.teamReplied')\n }\n return t(`warranty_claims.portal.event.${event.kind}`)\n}\n\nfunction buildActivityEntries(\n events: PortalClaimEvent[],\n attachments: PortalAttachment[],\n t: TranslateFn,\n): ActivityEntry[] {\n const eventEntries: ActivityEntry[] = events.map((event) => ({\n id: `event-${event.id}`,\n title: eventTitle(event, t),\n description: formatEventBody(event, t),\n createdAt: event.createdAt,\n }))\n const attachmentEntries: ActivityEntry[] = attachments.map((attachment) => ({\n id: `attachment-${attachment.id}`,\n title: t('warranty_claims.portal.tracker.event.attachmentAdded'),\n description: attachment.fileName,\n createdAt: attachment.createdAt,\n href: attachment.downloadUrl,\n attachment,\n }))\n return [...eventEntries, ...attachmentEntries].sort((left, right) => {\n const leftTime = left.createdAt ? new Date(left.createdAt).getTime() : 0\n const rightTime = right.createdAt ? new Date(right.createdAt).getTime() : 0\n return rightTime - leftTime\n })\n}\n\nconst DETAIL_MUTATION_CONTEXT_ID = 'warranty_claims.portal.claim.detail'\n\nconst SECTION_CLASS = 'flex w-full flex-col gap-3 border-t border-border px-6 pb-6 pt-5'\nconst SECTION_HEADING_CLASS = 'text-sm font-semibold text-foreground'\nconst DARK_BUTTON_CLASS = 'rounded-md px-3 py-2 text-sm font-medium'\n\nexport default function WarrantyClaimPortalDetailPage({ params }: Props) {\n const t = useT()\n const locale = useLocale()\n const { auth } = usePortalContext()\n const { user, loading } = auth\n const guardedMutation = useGuardedMutation<Record<string, unknown>>({\n contextId: DETAIL_MUTATION_CONTEXT_ID,\n blockedMessage: t('warranty_claims.portal.detail.blocked'),\n })\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [claim, setClaim] = React.useState<PortalClaim | null>(null)\n const [events, setEvents] = React.useState<PortalClaimEvent[]>([])\n const [attachments, setAttachments] = React.useState<PortalAttachment[]>([])\n const [reasonOptions, setReasonOptions] = React.useState<PortalOption[]>([])\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [notFound, setNotFound] = React.useState(false)\n const [comment, setComment] = React.useState('')\n const [commentSubmitting, setCommentSubmitting] = React.useState(false)\n const [fileInputKey, setFileInputKey] = React.useState(0)\n const [uploading, setUploading] = React.useState(false)\n const [attachmentMutationId, setAttachmentMutationId] = React.useState<string | null>(null)\n const [claimAction, setClaimAction] = React.useState<PortalClaimAction | null>(null)\n const [actionSubmitting, setActionSubmitting] = React.useState(false)\n const replacementInputRef = React.useRef<HTMLInputElement | null>(null)\n const replacementTargetRef = React.useRef<PortalAttachment | null>(null)\n const claimRequestSequenceRef = React.useRef(0)\n const eventsRequestSequenceRef = React.useRef(0)\n\n // Leaving an authenticated page for the login page crosses the public/authenticated\n // boundary, so it must be a full page load \u2014 see `replaceWithPageReload`.\n React.useEffect(() => {\n if (!loading && !user) {\n replaceWithPageReload(`/${params.orgSlug}/portal/login`)\n }\n }, [loading, user, params.orgSlug])\n\n const refreshEvents = React.useCallback(async (claimId: string) => {\n const requestSequence = ++eventsRequestSequenceRef.current\n const result = await apiCall<EventResponse>(`/api/warranty_claims/portal/events?claimId=${encodeURIComponent(claimId)}`)\n if (requestSequence === eventsRequestSequenceRef.current && result.ok && result.result) {\n setEvents(result.result.items)\n }\n }, [])\n\n const refreshAttachments = React.useCallback(async (claimId: string) => {\n const result = await apiCall<AttachmentResponse>(`/api/warranty_claims/portal/attachments?claimId=${encodeURIComponent(claimId)}`)\n if (result.ok && result.result) setAttachments(result.result.items)\n }, [])\n\n const loadClaim = React.useCallback(async (showLoading = true) => {\n const requestSequence = ++claimRequestSequenceRef.current\n if (showLoading) setIsLoading(true)\n setError(null)\n setNotFound(false)\n try {\n const detail = await apiCall<ClaimResponse>(`/api/warranty_claims/portal/claims/${encodeURIComponent(params.id)}`)\n if (requestSequence !== claimRequestSequenceRef.current) return\n if (detail.status === 404) {\n setClaim(null)\n setNotFound(true)\n return\n }\n if (!detail.ok || !detail.result) {\n setError(t('warranty_claims.portal.detail.loadError'))\n return\n }\n setClaim(detail.result.item)\n await Promise.all([\n refreshEvents(detail.result.item.id),\n refreshAttachments(detail.result.item.id),\n ])\n } catch {\n if (requestSequence === claimRequestSequenceRef.current) {\n setError(t('warranty_claims.portal.detail.loadError'))\n }\n } finally {\n if (requestSequence === claimRequestSequenceRef.current) setIsLoading(false)\n }\n }, [params.id, refreshAttachments, refreshEvents, t])\n\n React.useEffect(() => {\n if (user) void loadClaim()\n }, [loadClaim, user])\n\n usePortalAppEvent('warranty_claims.claim.portal_status_changed', (event) => {\n if (!user || event.payload.claimId !== params.id) return\n void loadClaim(false)\n })\n\n usePortalAppEvent('warranty_claims.claim.comment_added', (event) => {\n if (!user || event.payload.claimId !== params.id) return\n void refreshEvents(params.id).catch((error) => {\n getTelemetryRuntime()?.reportError(error, {\n module: 'warranty_claims',\n code: 'warranty_claims.portal_timeline_refresh_failed',\n attributes: { claimId: params.id },\n })\n setError(t('warranty_claims.portal.detail.loadError'))\n })\n })\n\n React.useEffect(() => {\n if (!user) return\n let cancelled = false\n apiCall<PortalOptionsResponse>('/api/warranty_claims/portal/options')\n .then((result) => {\n if (cancelled) return\n setReasonOptions(result.result?.ok ? result.result.result?.reasons ?? [] : [])\n })\n .catch(() => {})\n return () => {\n cancelled = true\n }\n }, [user])\n\n const submitComment = React.useCallback(async () => {\n if (!claim || commentSubmitting) return\n const body = comment.trim()\n if (!body) return\n setCommentSubmitting(true)\n setError(null)\n try {\n const result = await guardedMutation.runMutation({\n operation: () => apiCall<MutationOkResponse>('/api/warranty_claims/portal/events', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({ claimId: claim.id, body }),\n }),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'warranty_claims.claim_event',\n operation: 'portal_comment',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'warranty_claims.claim_event',\n resourceId: claim.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: { claimId: claim.id, body },\n })\n if (!result.ok || !result.result?.ok) {\n setError(t('warranty_claims.portal.detail.commentError'))\n return\n }\n setComment('')\n flash(t('warranty_claims.portal.detail.commentSuccess'), 'success')\n await refreshEvents(claim.id)\n } catch {\n setError(t('warranty_claims.portal.detail.commentError'))\n } finally {\n setCommentSubmitting(false)\n }\n }, [claim, comment, commentSubmitting, guardedMutation, refreshEvents, t])\n\n const uploadAttachment = React.useCallback(async (file: File, replacementTarget?: PortalAttachment) => {\n if (!claim || uploading) return\n const validationError = await validateAttachmentFile(file, t)\n if (validationError) {\n setError(validationError)\n flash(validationError, 'error')\n return\n }\n setUploading(true)\n setAttachmentMutationId(replacementTarget?.id ?? 'new')\n setError(null)\n try {\n const form = new FormData()\n form.set('claimId', claim.id)\n form.set('file', file)\n if (replacementTarget) form.set('attachmentId', replacementTarget.id)\n const result = await guardedMutation.runMutation({\n operation: () => apiCall<UploadResponse>('/api/warranty_claims/portal/attachments', {\n method: replacementTarget ? 'PUT' : 'POST',\n credentials: 'include',\n body: form,\n }),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'attachments.attachment',\n operation: replacementTarget ? 'portal_attachment_replace' : 'portal_attachment_upload',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'attachments.attachment',\n resourceId: replacementTarget?.id ?? claim.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: {\n claimId: claim.id,\n attachmentId: replacementTarget?.id,\n fileName: file.name,\n fileSize: file.size,\n },\n })\n if (!result.ok || !result.result?.ok) {\n setError(replacementTarget\n ? t('warranty_claims.portal.detail.attachmentReplaceError', 'Attachment replacement failed.')\n : t('warranty_claims.portal.detail.attachmentError'))\n return\n }\n flash(\n replacementTarget\n ? t('warranty_claims.portal.detail.attachmentReplaceSuccess', 'Attachment replaced.')\n : t('warranty_claims.portal.detail.attachmentSuccess'),\n 'success',\n )\n await refreshAttachments(claim.id)\n } catch {\n setError(replacementTarget\n ? t('warranty_claims.portal.detail.attachmentReplaceError', 'Attachment replacement failed.')\n : t('warranty_claims.portal.detail.attachmentError'))\n } finally {\n setUploading(false)\n setAttachmentMutationId(null)\n setFileInputKey((current) => current + 1)\n }\n }, [claim, guardedMutation, refreshAttachments, t, uploading])\n\n const deleteAttachment = React.useCallback(async (attachment: PortalAttachment) => {\n if (!claim || uploading) return\n const confirmed = await confirm({\n title: t('warranty_claims.portal.detail.attachmentDelete', 'Delete attachment'),\n text: t(\n 'warranty_claims.portal.detail.attachmentDeleteConfirm',\n 'Delete \"{name}\"? This action cannot be undone.',\n { name: attachment.fileName },\n ),\n confirmText: t('warranty_claims.portal.detail.attachmentDelete', 'Delete attachment'),\n cancelText: t('warranty_claims.portal.detail.dialogCancel', 'Cancel'),\n variant: 'destructive',\n })\n if (!confirmed) return\n setUploading(true)\n setAttachmentMutationId(attachment.id)\n setError(null)\n try {\n const result = await guardedMutation.runMutation({\n operation: () => apiCall<MutationOkResponse>(\n `/api/warranty_claims/portal/attachments?attachmentId=${encodeURIComponent(attachment.id)}`,\n { method: 'DELETE', credentials: 'include' },\n ),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'attachments.attachment',\n operation: 'portal_attachment_delete',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'attachments.attachment',\n resourceId: attachment.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: { claimId: claim.id, attachmentId: attachment.id },\n })\n if (!result.ok || !result.result?.ok) {\n setError(t('warranty_claims.portal.detail.attachmentDeleteError', 'Attachment deletion failed.'))\n return\n }\n flash(t('warranty_claims.portal.detail.attachmentDeleteSuccess', 'Attachment deleted.'), 'success')\n await refreshAttachments(claim.id)\n } catch {\n setError(t('warranty_claims.portal.detail.attachmentDeleteError', 'Attachment deletion failed.'))\n } finally {\n setUploading(false)\n setAttachmentMutationId(null)\n }\n }, [claim, confirm, guardedMutation, refreshAttachments, t, uploading])\n\n const requestAttachmentReplacement = React.useCallback((attachment: PortalAttachment) => {\n if (uploading) return\n replacementTargetRef.current = attachment\n replacementInputRef.current?.click()\n }, [uploading])\n\n const runClaimAction = React.useCallback(async (action: PortalClaimAction) => {\n if (!claim || actionSubmitting) return\n setActionSubmitting(true)\n setError(null)\n try {\n const endpoint = `/api/warranty_claims/portal/claims/${encodeURIComponent(claim.id)}/${action}`\n const result = await guardedMutation.runMutation({\n operation: () => withScopedApiRequestHeaders(\n buildOptimisticLockHeader(claim.updatedAt),\n () => apiCall<ClaimActionResponse>(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({}),\n }),\n ),\n context: {\n moduleId: 'warranty_claims',\n entityId: 'warranty_claims.claim',\n operation: action === 'submit' ? 'portal_submit' : 'portal_withdraw',\n claimId: claim.id,\n formId: DETAIL_MUTATION_CONTEXT_ID,\n resourceKind: 'warranty_claims.claim',\n resourceId: claim.id,\n retryLastMutation: guardedMutation.retryLastMutation,\n },\n mutationPayload: { claimId: claim.id, action },\n })\n if (!result.ok || !result.result?.ok) {\n const message = result.status === 409\n ? t('warranty_claims.portal.detail.actionConflict', 'This claim changed in the meantime. It has been reloaded \u2014 please try again.')\n : action === 'submit'\n ? t('warranty_claims.portal.detail.submitError', 'The claim could not be submitted.')\n : t('warranty_claims.portal.detail.withdrawError', 'The claim could not be withdrawn.')\n setError(message)\n flash(message, 'error')\n if (result.status === 409) {\n setClaimAction(null)\n await loadClaim()\n }\n return\n }\n setClaimAction(null)\n flash(\n action === 'submit'\n ? t('warranty_claims.portal.detail.submitSuccess', 'Your claim has been submitted.')\n : t('warranty_claims.portal.detail.withdrawSuccess', 'Your claim has been withdrawn.'),\n 'success',\n )\n await loadClaim()\n } catch {\n const message = action === 'submit'\n ? t('warranty_claims.portal.detail.submitError', 'The claim could not be submitted.')\n : t('warranty_claims.portal.detail.withdrawError', 'The claim could not be withdrawn.')\n setError(message)\n flash(message, 'error')\n } finally {\n setActionSubmitting(false)\n }\n }, [actionSubmitting, claim, guardedMutation, loadClaim, t])\n\n const handleSelectedFileChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0] ?? null\n if (!file) return\n void uploadAttachment(file)\n }, [uploadAttachment])\n\n const handleReplacementFileChange = React.useCallback((event: React.ChangeEvent<HTMLInputElement>) => {\n const replacementTarget = replacementTargetRef.current\n replacementTargetRef.current = null\n const file = event.target.files?.[0] ?? null\n event.target.value = ''\n if (!replacementTarget || !file) return\n void uploadAttachment(file, replacementTarget)\n }, [uploadAttachment])\n\n const handleCommentSubmit = React.useCallback((event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n void submitComment()\n }, [submitComment])\n\n const handleCommentKeyDown = React.useCallback((event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n void submitComment()\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n setComment('')\n }\n }, [submitComment])\n\n if (loading) {\n return <div className=\"flex items-center justify-center py-20\"><Spinner /></div>\n }\n\n if (!user) return null\n\n if (isLoading) {\n return <LoadingMessage label={t('warranty_claims.portal.detail.loading')} />\n }\n\n if (notFound) {\n return (\n <PortalEmptyState\n icon={<ShieldCheck className=\"size-5\" />}\n title={t('warranty_claims.portal.detail.notFoundTitle')}\n description={t('warranty_claims.portal.detail.notFoundDescription')}\n action={\n <Button asChild>\n <Link href={`/${params.orgSlug}/portal/claims`}>\n <ArrowLeft className=\"size-4\" aria-hidden=\"true\" />\n {t('warranty_claims.portal.detail.back')}\n </Link>\n </Button>\n }\n />\n )\n }\n\n if (!claim) {\n return <ErrorMessage label={error ?? t('warranty_claims.portal.detail.loadError')} />\n }\n\n const trackerSteps = buildTrackerSteps(claim, t, locale)\n const isDraft = claim.status === 'draft'\n const canWithdraw = claim.status === 'draft' || claim.status === 'submitted'\n const tone = bannerTone(claim.status)\n const toneClasses = BANNER_TONE_CLASSES[tone]\n const BannerIcon = tone === 'error' ? CircleAlert : tone === 'warning' ? TriangleAlert : Info\n const activityEntries = buildActivityEntries(events, attachments, t)\n\n return (\n <div className=\"mx-auto flex w-full max-w-3xl flex-col gap-4\">\n <Link\n href={`/${params.orgSlug}/portal/claims`}\n className=\"text-sm font-medium text-muted-foreground transition-colors hover:text-foreground\"\n >\n {'\u2190'} {t('warranty_claims.portal.listTitle')}\n </Link>\n\n {error ? <ErrorMessage label={error} /> : null}\n\n <div className=\"w-full overflow-hidden rounded-lg border border-border bg-card\">\n <div className=\"flex w-full flex-col gap-3 px-6 pb-5 pt-6\">\n <div className=\"flex w-full items-center gap-2\">\n <div className=\"flex items-center gap-2 rounded-sm border border-border bg-background px-3 py-1\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n {t(`warranty_claims.claimType.${claim.claimType}`)}\n </span>\n <span className=\"text-sm font-semibold text-foreground\">\n {claim.claimNumber}\n </span>\n </div>\n <div className=\"min-w-px flex-1\" />\n {isDraft ? (\n <Button\n type=\"button\"\n size=\"sm\"\n className={DARK_BUTTON_CLASS}\n onClick={() => setClaimAction('submit')}\n disabled={actionSubmitting}\n >\n {t('warranty_claims.portal.submit')}\n </Button>\n ) : null}\n {canWithdraw ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"destructive\"\n className=\"rounded-md px-3 py-2 text-sm font-medium\"\n onClick={() => setClaimAction('withdraw')}\n disabled={actionSubmitting}\n >\n {t('warranty_claims.portal.detail.withdraw', 'Withdraw claim')}\n </Button>\n ) : null}\n </div>\n <h1 className=\"text-xl font-bold text-foreground\">\n {buildClaimTitle(claim, reasonOptions, t)}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {buildClaimSubtitle(claim, t, locale)}\n </p>\n </div>\n\n <div className=\"w-full px-6 pb-6\">\n <div className={`flex w-full items-start gap-3 rounded-lg px-4 py-3 ${toneClasses.wrapper}`}>\n <BannerIcon className={`mt-px size-4 shrink-0 ${toneClasses.icon}`} aria-hidden=\"true\" />\n <p className={`min-w-px flex-1 text-sm ${toneClasses.text}`}>\n {bannerMessage(claim, t)}\n </p>\n </div>\n </div>\n\n <div className=\"flex w-full flex-col px-6 pb-6\">\n {trackerSteps.map((step, index) => (\n <div key={step.id} className=\"flex w-full items-stretch gap-4\">\n <div className=\"flex flex-col items-center\">\n {step.state === 'complete' ? (\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-status-success-icon\">\n <Check className=\"size-3 text-status-success-bg\" strokeWidth={2.5} aria-hidden=\"true\" />\n </span>\n ) : step.state === 'current' ? (\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full bg-primary text-xs font-semibold text-primary-foreground\">\n {index + 1}\n </span>\n ) : (\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-full border border-border text-xs font-semibold text-muted-foreground\">\n {index + 1}\n </span>\n )}\n {index < trackerSteps.length - 1 ? (\n <span\n className={`min-h-2 w-px flex-1 ${step.state === 'complete' ? 'bg-status-success-icon' : 'bg-border'}`}\n />\n ) : null}\n </div>\n <div className=\"flex min-w-px flex-1 flex-col gap-1 pb-4\">\n <div className=\"flex w-full items-center gap-2\">\n <p\n className={\n step.state === 'pending'\n ? 'text-sm font-medium text-muted-foreground'\n : 'text-sm font-semibold text-foreground'\n }\n >\n {step.title}\n </p>\n <div className=\"min-w-px flex-1\" />\n {step.dateLabel ? (\n <p className=\"text-xs text-muted-foreground\">{step.dateLabel}</p>\n ) : null}\n </div>\n <p className=\"w-full text-xs text-muted-foreground\">{step.description}</p>\n </div>\n </div>\n ))}\n </div>\n\n <div className={SECTION_CLASS}>\n <p className={SECTION_HEADING_CLASS}>{t('warranty_claims.portal.tracker.items')}</p>\n {claim.lines.length > 0 ? (\n claim.lines.map((line) => (\n <div\n key={line.id}\n className=\"flex w-full items-center gap-3 rounded-lg border border-border px-4 py-3\"\n >\n <div className=\"flex min-w-px flex-1 flex-col gap-1\">\n <p className=\"text-sm font-medium text-foreground\">\n {line.productName ?? line.sku ?? t('warranty_claims.portal.value.unnamedLine')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {line.sku\n ? t('warranty_claims.portal.tracker.itemMeta', {\n sku: line.sku,\n qty: formatQuantity(line.qtyClaimed, t('warranty_claims.portal.value.notAvailable')),\n })\n : t('warranty_claims.portal.tracker.itemQty', {\n qty: formatQuantity(line.qtyClaimed, t('warranty_claims.portal.value.notAvailable')),\n })}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2 rounded-sm border border-border bg-background px-2 py-1\">\n <span\n className={`size-2 shrink-0 rounded-full ${LINE_STATUS_DOT_CLASSES[line.lineStatus] ?? 'bg-status-neutral-icon'}`}\n aria-hidden=\"true\"\n />\n <span className=\"text-xs font-medium text-foreground\">\n {t(`warranty_claims.lineStatus.${line.lineStatus}`)}\n </span>\n </div>\n </div>\n ))\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('warranty_claims.portal.detail.linesEmpty.description')}\n </p>\n )}\n </div>\n\n <div className={SECTION_CLASS}>\n <p className={SECTION_HEADING_CLASS}>{t('warranty_claims.portal.tracker.activity')}</p>\n {activityEntries.length > 0 ? (\n activityEntries.map((entry) => (\n <div key={entry.id} className=\"flex w-full items-start gap-3\">\n <span className=\"mt-1 size-2 shrink-0 rounded-full bg-status-neutral-icon\" aria-hidden=\"true\" />\n <div className=\"flex min-w-px flex-1 flex-col gap-px\">\n <div className=\"flex w-full items-start gap-2\">\n <p className=\"text-sm font-medium text-foreground\">{entry.title}</p>\n <div className=\"min-w-px flex-1\" />\n {entry.createdAt ? (\n <p className=\"text-xs text-muted-foreground\">{formatShortDate(entry.createdAt, locale)}</p>\n ) : null}\n </div>\n {entry.href ? (\n <div className=\"flex flex-wrap items-center gap-2\">\n <a\n href={entry.href}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"min-w-0 whitespace-pre-wrap text-xs text-muted-foreground underline-offset-2 hover:underline\"\n >\n {entry.description}\n </a>\n {entry.attachment ? (\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"px-2 text-xs\"\n aria-label={t(\n 'warranty_claims.portal.detail.attachmentReplaceNamed',\n 'Replace {name}',\n { name: entry.attachment.fileName },\n )}\n onClick={() => requestAttachmentReplacement(entry.attachment!)}\n disabled={uploading}\n >\n {attachmentMutationId === entry.attachment.id ? (\n <Spinner className=\"size-3\" />\n ) : (\n <RefreshCw className=\"size-3\" aria-hidden=\"true\" />\n )}\n {t('warranty_claims.portal.detail.attachmentReplace', 'Replace')}\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"px-2 text-xs text-status-error-text\"\n aria-label={t(\n 'warranty_claims.portal.detail.attachmentDeleteNamed',\n 'Delete {name}',\n { name: entry.attachment.fileName },\n )}\n onClick={() => { void deleteAttachment(entry.attachment!) }}\n disabled={uploading}\n >\n <Trash2 className=\"size-3\" aria-hidden=\"true\" />\n {t('warranty_claims.portal.detail.attachmentDelete', 'Delete attachment')}\n </Button>\n </div>\n ) : null}\n </div>\n ) : (\n <p className=\"whitespace-pre-wrap text-xs text-muted-foreground\">{entry.description}</p>\n )}\n </div>\n </div>\n ))\n ) : (\n <p className=\"text-xs text-muted-foreground\">\n {t('warranty_claims.portal.detail.timelineEmpty.description')}\n </p>\n )}\n <input\n ref={replacementInputRef}\n type=\"file\"\n accept={ATTACHMENT_ACCEPT_TYPES}\n onChange={handleReplacementFileChange}\n disabled={uploading}\n className=\"sr-only\"\n aria-label={t('warranty_claims.portal.detail.attachmentChooseReplacement', 'Choose replacement file')}\n />\n </div>\n\n <div className={SECTION_CLASS}>\n <p className={SECTION_HEADING_CLASS}>{t('warranty_claims.portal.tracker.message')}</p>\n <form id=\"warranty-claim-comment-box\" className=\"flex w-full flex-col gap-3\" onSubmit={handleCommentSubmit}>\n <Textarea\n value={comment}\n onChange={(event) => setComment(event.target.value)}\n onKeyDown={handleCommentKeyDown}\n placeholder={t('warranty_claims.portal.tracker.messagePlaceholder')}\n disabled={commentSubmitting}\n maxLength={8000}\n className=\"h-40 resize-none rounded-lg border-input bg-background px-3 py-2 text-sm shadow-xs placeholder:text-muted-foreground\"\n />\n <div className=\"flex w-full items-center gap-2\">\n <label className={`flex items-center gap-2 ${uploading ? 'cursor-default opacity-70' : 'cursor-pointer'}`}>\n {uploading ? (\n <Spinner className=\"size-4\" />\n ) : (\n <Paperclip className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n )}\n <span className=\"text-xs font-medium text-muted-foreground transition-colors hover:text-foreground\">\n {uploading\n ? t('warranty_claims.portal.detail.uploading')\n : t('warranty_claims.portal.tracker.attachFiles')}\n </span>\n <input\n key={fileInputKey}\n type=\"file\"\n accept={ATTACHMENT_ACCEPT_TYPES}\n onChange={handleSelectedFileChange}\n disabled={uploading}\n className=\"sr-only\"\n />\n </label>\n <div className=\"min-w-px flex-1\" />\n <Button type=\"submit\" size=\"sm\" className={DARK_BUTTON_CLASS} disabled={commentSubmitting || !comment.trim()}>\n {commentSubmitting\n ? t('warranty_claims.portal.tracker.sending')\n : t('warranty_claims.portal.tracker.send')}\n </Button>\n </div>\n </form>\n </div>\n </div>\n\n <Dialog\n open={claimAction !== null}\n onOpenChange={(open) => {\n if (!open && !actionSubmitting) setClaimAction(null)\n }}\n >\n <DialogContent\n className=\"max-w-lg\"\n onKeyDown={(event) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n if (claimAction && !actionSubmitting) void runClaimAction(claimAction)\n }\n if (event.key === 'Escape' && !actionSubmitting) {\n setClaimAction(null)\n }\n }}\n >\n <DialogHeader>\n <DialogTitle>\n {claimAction === 'withdraw'\n ? t('warranty_claims.portal.detail.withdrawDialog.title', 'Withdraw this claim?')\n : t('warranty_claims.portal.detail.submitDialog.title', 'Submit this claim?')}\n </DialogTitle>\n <DialogDescription>\n {claimAction === 'withdraw'\n ? t('warranty_claims.portal.detail.withdrawDialog.description', 'The claim will be cancelled and cannot be reopened. This cannot be undone.')\n : t('warranty_claims.portal.detail.submitDialog.description', 'Your claim will be sent to our support team for review. You can still add comments and photos afterwards.')}\n </DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setClaimAction(null)}\n disabled={actionSubmitting}\n >\n {t('warranty_claims.portal.detail.dialogCancel', 'Cancel')}\n </Button>\n <Button\n type=\"button\"\n variant={claimAction === 'withdraw' ? 'destructive' : 'default'}\n onClick={() => {\n if (claimAction) void runClaimAction(claimAction)\n }}\n disabled={actionSubmitting}\n >\n {claimAction === 'withdraw'\n ? (actionSubmitting\n ? t('warranty_claims.portal.detail.withdrawing', 'Withdrawing...')\n : t('warranty_claims.portal.detail.withdrawConfirm', 'Withdraw claim'))\n : (actionSubmitting\n ? t('warranty_claims.portal.detail.submitting', 'Submitting...')\n : t('warranty_claims.portal.detail.submitConfirm', 'Submit claim'))}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["error"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
2
|
+
async function loadClaimLines(claimId, errorMessage) {
|
|
3
|
+
const readPage = (page) => readApiResultOrThrow(
|
|
4
|
+
`/api/warranty_claims/lines?claimId=${encodeURIComponent(claimId)}&page=${page}&pageSize=100&sortField=lineNo&sortDir=asc`,
|
|
5
|
+
void 0,
|
|
6
|
+
{ fallback: { items: [] }, errorMessage }
|
|
7
|
+
);
|
|
8
|
+
const firstPage = await readPage(1);
|
|
9
|
+
const items = [...firstPage.items ?? []];
|
|
10
|
+
const totalPages = firstPage.totalPages ?? 1;
|
|
11
|
+
for (let page = 2; page <= totalPages; page += 1) {
|
|
12
|
+
const payload = await readPage(page);
|
|
13
|
+
items.push(...payload.items ?? []);
|
|
14
|
+
}
|
|
15
|
+
return { items };
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
loadClaimLines
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=claimLines.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/warranty_claims/lib/claimLines.ts"],
|
|
4
|
+
"sourcesContent": ["import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\n\ntype ClaimLinesPage = {\n items?: unknown[]\n totalPages?: number\n}\n\nexport async function loadClaimLines(claimId: string, errorMessage: string): Promise<{ items: unknown[] }> {\n const readPage = (page: number) => readApiResultOrThrow<ClaimLinesPage>(\n `/api/warranty_claims/lines?claimId=${encodeURIComponent(claimId)}&page=${page}&pageSize=100&sortField=lineNo&sortDir=asc`,\n undefined,\n { fallback: { items: [] }, errorMessage },\n )\n const firstPage = await readPage(1)\n const items = [...(firstPage.items ?? [])]\n const totalPages = firstPage.totalPages ?? 1\n for (let page = 2; page <= totalPages; page += 1) {\n const payload = await readPage(page)\n items.push(...(payload.items ?? []))\n }\n return { items }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,4BAA4B;AAOrC,eAAsB,eAAe,SAAiB,cAAqD;AACzG,QAAM,WAAW,CAAC,SAAiB;AAAA,IACjC,sCAAsC,mBAAmB,OAAO,CAAC,SAAS,IAAI;AAAA,IAC9E;AAAA,IACA,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,GAAG,aAAa;AAAA,EAC1C;AACA,QAAM,YAAY,MAAM,SAAS,CAAC;AAClC,QAAM,QAAQ,CAAC,GAAI,UAAU,SAAS,CAAC,CAAE;AACzC,QAAM,aAAa,UAAU,cAAc;AAC3C,WAAS,OAAO,GAAG,QAAQ,YAAY,QAAQ,GAAG;AAChD,UAAM,UAAU,MAAM,SAAS,IAAI;AACnC,UAAM,KAAK,GAAI,QAAQ,SAAS,CAAC,CAAE;AAAA,EACrC;AACA,SAAO,EAAE,MAAM;AACjB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.10.1-develop.
|
|
3
|
+
"version": "0.10.1-develop.7894.1.2c464c73e3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -266,16 +266,16 @@
|
|
|
266
266
|
"zod": "^4.6.6"
|
|
267
267
|
},
|
|
268
268
|
"peerDependencies": {
|
|
269
|
-
"@open-mercato/ai-assistant": "0.10.1-develop.
|
|
270
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
271
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
269
|
+
"@open-mercato/ai-assistant": "0.10.1-develop.7894.1.2c464c73e3",
|
|
270
|
+
"@open-mercato/shared": "0.10.1-develop.7894.1.2c464c73e3",
|
|
271
|
+
"@open-mercato/ui": "0.10.1-develop.7894.1.2c464c73e3",
|
|
272
272
|
"react": "^19.0.0",
|
|
273
273
|
"react-dom": "^19.0.0"
|
|
274
274
|
},
|
|
275
275
|
"devDependencies": {
|
|
276
|
-
"@open-mercato/ai-assistant": "0.10.1-develop.
|
|
277
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
278
|
-
"@open-mercato/ui": "0.10.1-develop.
|
|
276
|
+
"@open-mercato/ai-assistant": "0.10.1-develop.7894.1.2c464c73e3",
|
|
277
|
+
"@open-mercato/shared": "0.10.1-develop.7894.1.2c464c73e3",
|
|
278
|
+
"@open-mercato/ui": "0.10.1-develop.7894.1.2c464c73e3",
|
|
279
279
|
"@testing-library/dom": "^10.4.1",
|
|
280
280
|
"@testing-library/jest-dom": "^7.0.1",
|
|
281
281
|
"@testing-library/react": "^16.3.3",
|
|
@@ -53,10 +53,16 @@ const auditActionQuerySchema = z.object({
|
|
|
53
53
|
.default('false')
|
|
54
54
|
.describe('When `true`, only undoable actions are returned')
|
|
55
55
|
.optional(),
|
|
56
|
-
limit: z
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
limit: z
|
|
57
|
+
.string()
|
|
58
|
+
.describe('Maximum number of records to return (default 50, max 1000). Ignored when `pageSize` is sent.')
|
|
59
|
+
.optional(),
|
|
60
|
+
offset: z
|
|
61
|
+
.string()
|
|
62
|
+
.describe('Zero-based record offset for pagination (legacy — prefer page/pageSize). Takes precedence over `page` when both are sent.')
|
|
63
|
+
.optional(),
|
|
64
|
+
page: z.string().describe('Page number (default 1). Ignored when `offset` is sent.').optional(),
|
|
65
|
+
pageSize: z.string().describe('Page size (default 50, max 200). Takes precedence over `limit` when both are sent.').optional(),
|
|
60
66
|
sortField: z
|
|
61
67
|
.enum(SORT_FIELDS)
|
|
62
68
|
.describe('Sort field: `createdAt`, `user`, `action`, `field`, or `source`.')
|
|
@@ -113,18 +119,11 @@ function parseDate(value: string | null): Date | undefined {
|
|
|
113
119
|
return new Date(ts)
|
|
114
120
|
}
|
|
115
121
|
|
|
116
|
-
function
|
|
117
|
-
if (
|
|
118
|
-
const value = Number(param)
|
|
119
|
-
if (!Number.isFinite(value)) return 50
|
|
120
|
-
return Math.min(Math.max(Math.trunc(value), 1), 1000)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function parseOffset(param: string | null): number {
|
|
124
|
-
if (!param) return 0
|
|
122
|
+
function parseOptionalInteger(param: string | null, { min, max }: { min: number; max: number }): number | undefined {
|
|
123
|
+
if (param === null || param.trim() === '') return undefined
|
|
125
124
|
const value = Number(param)
|
|
126
|
-
if (!Number.isFinite(value)) return
|
|
127
|
-
return Math.max(Math.trunc(value),
|
|
125
|
+
if (!Number.isFinite(value)) return undefined
|
|
126
|
+
return Math.min(Math.max(Math.trunc(value), min), max)
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
function splitCsv(value: string | null): string[] {
|
|
@@ -141,15 +140,6 @@ function parseActionTypes(param: string | null) {
|
|
|
141
140
|
)
|
|
142
141
|
}
|
|
143
142
|
|
|
144
|
-
function parseNumber(param: string | null, { min, max, fallback }: { min: number; max: number; fallback: number }) {
|
|
145
|
-
if (!param) return fallback
|
|
146
|
-
const value = Number(param)
|
|
147
|
-
if (!Number.isFinite(value)) return fallback
|
|
148
|
-
const normalized = Math.trunc(value)
|
|
149
|
-
if (Number.isNaN(normalized)) return fallback
|
|
150
|
-
return Math.min(Math.max(normalized, min), max)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
143
|
export async function GET(req: Request) {
|
|
154
144
|
const auth = await getAuthFromRequest(req)
|
|
155
145
|
if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
@@ -182,10 +172,10 @@ export async function GET(req: Request) {
|
|
|
182
172
|
const includeRelated = parseBooleanToken(url.searchParams.get('includeRelated')) === true
|
|
183
173
|
const includeTotal = parseBooleanToken(url.searchParams.get('includeTotal')) === true
|
|
184
174
|
const undoableOnly = parseBooleanToken(url.searchParams.get('undoableOnly')) === true
|
|
185
|
-
const limit =
|
|
186
|
-
const offset =
|
|
187
|
-
const page =
|
|
188
|
-
const pageSize =
|
|
175
|
+
const limit = parseOptionalInteger(url.searchParams.get('limit'), { min: 1, max: 1000 })
|
|
176
|
+
const offset = parseOptionalInteger(url.searchParams.get('offset'), { min: 0, max: Number.MAX_SAFE_INTEGER })
|
|
177
|
+
const page = parseOptionalInteger(url.searchParams.get('page'), { min: 1, max: 1000000 })
|
|
178
|
+
const pageSize = parseOptionalInteger(url.searchParams.get('pageSize'), { min: 1, max: 200 })
|
|
189
179
|
const sortField = SORT_FIELDS.find((value) => value === url.searchParams.get('sortField')) ?? 'createdAt'
|
|
190
180
|
const sortDir = SORT_DIRECTIONS.find((value) => value === url.searchParams.get('sortDir')) ?? 'desc'
|
|
191
181
|
const before = parseDate(url.searchParams.get('before'))
|