refactor(disputes): align type with backend and derive timeline in page
This commit is contained in:
@@ -44,15 +44,7 @@ const disputeStatusVariants: Record<string, StatusBadgeProps["status"]> = {
|
|||||||
appealed: "info",
|
appealed: "info",
|
||||||
}
|
}
|
||||||
|
|
||||||
function deriveMinimalTimeline<TCreatedAt>(dispute: {
|
function deriveMinimalTimeline(dispute: { id: string; status: string; createdAt: string }) {
|
||||||
id: string
|
|
||||||
status: string
|
|
||||||
createdAt: TCreatedAt
|
|
||||||
timeline?: { id: string; content: string; createdAt: TCreatedAt }[]
|
|
||||||
}) {
|
|
||||||
const existing = dispute.timeline
|
|
||||||
if (existing?.length) return existing
|
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{ status: "open", content: "争议已提交" },
|
{ status: "open", content: "争议已提交" },
|
||||||
{ status: "reviewing", content: "平台审核中" },
|
{ status: "reviewing", content: "平台审核中" },
|
||||||
|
|||||||
+9
-60
@@ -4,20 +4,6 @@ import type { Dispute } from "@/lib/types"
|
|||||||
|
|
||||||
import { httpJson } from "./http"
|
import { httpJson } from "./http"
|
||||||
|
|
||||||
export type DisputeTimelineItem = {
|
|
||||||
id: string
|
|
||||||
content: string
|
|
||||||
createdAt: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DisputeRecord = Dispute & {
|
|
||||||
respondentReason?: string
|
|
||||||
respondentEvidence: string[]
|
|
||||||
appealReason?: string
|
|
||||||
appealedAt?: string
|
|
||||||
timeline: DisputeTimelineItem[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ListDisputesOptions = {
|
export type ListDisputesOptions = {
|
||||||
offset?: number
|
offset?: number
|
||||||
limit?: number
|
limit?: number
|
||||||
@@ -64,49 +50,12 @@ function unwrapDispute(value: unknown): unknown {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
function deriveMinimalTimeline(dispute: {
|
function normalizeDispute(value: unknown): Dispute {
|
||||||
id: string
|
const dispute = unwrapDispute(value) as Dispute
|
||||||
status: string
|
|
||||||
createdAt: string
|
|
||||||
timeline?: DisputeTimelineItem[]
|
|
||||||
}): DisputeTimelineItem[] {
|
|
||||||
const existing = dispute.timeline
|
|
||||||
if (existing?.length) return existing
|
|
||||||
|
|
||||||
const steps = [
|
|
||||||
{ status: "open", content: "争议已提交" },
|
|
||||||
{ status: "reviewing", content: "平台审核中" },
|
|
||||||
{ status: "resolved", content: "争议已解决" },
|
|
||||||
{ status: "appealed", content: "已发起申诉" },
|
|
||||||
]
|
|
||||||
|
|
||||||
const currentIndex = steps.findIndex((step) => step.status === dispute.status)
|
|
||||||
const lastIndex = currentIndex >= 0 ? currentIndex : 0
|
|
||||||
return steps.slice(0, lastIndex + 1).map((step) => ({
|
|
||||||
id: `${dispute.id}-${step.status}`,
|
|
||||||
content: step.content,
|
|
||||||
createdAt: dispute.createdAt,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeDisputeRecord(value: unknown): DisputeRecord {
|
|
||||||
const dispute = unwrapDispute(value) as DisputeRecord
|
|
||||||
const respondentEvidence = Array.isArray(dispute.respondentEvidence)
|
|
||||||
? dispute.respondentEvidence
|
|
||||||
: []
|
|
||||||
const evidence = Array.isArray(dispute.evidence) ? dispute.evidence : []
|
|
||||||
const timeline = deriveMinimalTimeline({
|
|
||||||
id: dispute.id,
|
|
||||||
status: dispute.status,
|
|
||||||
createdAt: dispute.createdAt,
|
|
||||||
timeline: dispute.timeline,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...dispute,
|
...dispute,
|
||||||
evidence,
|
evidence: Array.isArray(dispute.evidence) ? dispute.evidence : [],
|
||||||
respondentEvidence,
|
respondentEvidence: Array.isArray(dispute.respondentEvidence) ? dispute.respondentEvidence : [],
|
||||||
timeline,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,20 +67,20 @@ function denyFromError(error: unknown): ApiDecision {
|
|||||||
return deny(apiError.code, apiError.msg)
|
return deny(apiError.code, apiError.msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listDisputes(options?: ListDisputesOptions): Promise<DisputeRecord[]> {
|
export async function listDisputes(options?: ListDisputesOptions): Promise<Dispute[]> {
|
||||||
const res = await httpJson<Paginated<DisputeRecord> | DisputeRecord[]>(
|
const res = await httpJson<Paginated<Dispute> | Dispute[]>(
|
||||||
withOffsetLimit("/api/v1/disputes", options),
|
withOffsetLimit("/api/v1/disputes", options),
|
||||||
{ cache: "no-store" },
|
{ cache: "no-store" },
|
||||||
)
|
)
|
||||||
return unwrapItems<DisputeRecord>(res).map((item) => normalizeDisputeRecord(item))
|
return unwrapItems<Dispute>(res).map((item) => normalizeDispute(item))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getDisputeByOrderId(orderId: string): Promise<DisputeRecord | undefined> {
|
export async function getDisputeByOrderId(orderId: string): Promise<Dispute | undefined> {
|
||||||
try {
|
try {
|
||||||
const res = await httpJson<unknown>(`/api/v1/orders/${encodeURIComponent(orderId)}/dispute`, {
|
const res = await httpJson<unknown>(`/api/v1/orders/${encodeURIComponent(orderId)}/dispute`, {
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
})
|
})
|
||||||
return normalizeDisputeRecord(res)
|
return normalizeDispute(res)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const apiError = isApiError(error) ? error : toApiError(error)
|
const apiError = isApiError(error) ? error : toApiError(error)
|
||||||
if (apiError.code === 404) return undefined
|
if (apiError.code === 404) return undefined
|
||||||
|
|||||||
@@ -120,11 +120,21 @@ export interface Review {
|
|||||||
export interface Dispute {
|
export interface Dispute {
|
||||||
id: SnowflakeId
|
id: SnowflakeId
|
||||||
orderId: SnowflakeId
|
orderId: SnowflakeId
|
||||||
|
initiatorId?: SnowflakeId
|
||||||
|
initiatorName?: string
|
||||||
|
respondentId?: SnowflakeId
|
||||||
reason: string
|
reason: string
|
||||||
evidence: string[]
|
evidence: string[]
|
||||||
status: "open" | "reviewing" | "resolved" | "appealed"
|
status: "open" | "reviewing" | "resolved" | "appealed"
|
||||||
result?: "full_refund" | "full_payment" | "partial_refund"
|
result?: "full_refund" | "full_payment" | "partial_refund"
|
||||||
|
respondentReason?: string
|
||||||
|
respondentEvidence: string[]
|
||||||
|
appealReason?: string
|
||||||
|
appealedAt?: string
|
||||||
|
resolvedBy?: SnowflakeId
|
||||||
|
resolvedAt?: string
|
||||||
createdAt: string
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatParticipant {
|
export interface ChatParticipant {
|
||||||
|
|||||||
Reference in New Issue
Block a user