前端调整V4预订事项用户化展示
This commit is contained in:
@@ -128,27 +128,13 @@
|
||||
<template v-if="hasNextV4Action && nextV4Action">
|
||||
<dl class="next-action-facts">
|
||||
<div>
|
||||
<dt>{{ t('order.nextV4ActionType') }}</dt>
|
||||
<dd>{{ formatNextActionType(nextV4Action.action_type) }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ t('order.nextV4ActionStatus') }}</dt>
|
||||
<dd>
|
||||
<ReservationStatusBadge
|
||||
v-if="nextV4Action.action_status"
|
||||
:status="nextV4Action.action_status"
|
||||
/>
|
||||
<span v-else>-</span>
|
||||
</dd>
|
||||
<dt>{{ t('order.nextStep') }}</dt>
|
||||
<dd>{{ nextActionLabel }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ t('order.openOrderTaskCount') }}</dt>
|
||||
<dd>{{ nextV4Action.open_order_task_count ?? 0 }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ t('order.nextV4CardId') }}</dt>
|
||||
<dd>{{ nextV4Action.card_id ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<RouterLink
|
||||
class="primary-link"
|
||||
@@ -200,10 +186,6 @@
|
||||
<dt>{{ t('order.conversationMessageCount') }}</dt>
|
||||
<dd>{{ sourceMessage.conversation_message_count ?? '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ t('order.externalConversationId') }}</dt>
|
||||
<dd>{{ sourceMessage.external_conversation_id ?? '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<RouterLink
|
||||
class="source-link"
|
||||
@@ -284,6 +266,7 @@
|
||||
v-if="item.cards.length"
|
||||
class="card-summary-list"
|
||||
>
|
||||
<h3>{{ t('order.cardStatusSummary') }}</h3>
|
||||
<article
|
||||
v-for="card in item.cards"
|
||||
:key="card.card_id"
|
||||
@@ -292,15 +275,10 @@
|
||||
<div class="card-summary__heading">
|
||||
<span>
|
||||
<strong>{{ cardTimelineTitle(card) }}</strong>
|
||||
<small>{{ card.event_type ?? '-' }}</small>
|
||||
</span>
|
||||
<ReservationStatusBadge :status="card.card_status" />
|
||||
</div>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>{{ t('order.cardId') }}</dt>
|
||||
<dd>{{ card.card_id }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{{ t('order.reviewStatus') }}</dt>
|
||||
<dd>
|
||||
@@ -349,7 +327,6 @@ import { fetchReservationOrderDetail } from '@/services/reservationService'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
import type {
|
||||
ReservationOrderDetailResult,
|
||||
ReservationOrderV4NextActionResult,
|
||||
ReservationOrderV4RoomSummary,
|
||||
ReservationOrderV4TaskCardTimelineItem,
|
||||
ReservationV4OrderTaskTimelineItem,
|
||||
@@ -418,6 +395,19 @@ const overviewStatuses = computed(() => {
|
||||
{ label: t('order.paymentCardStatus'), status: overview?.payment_card_status ?? null },
|
||||
]
|
||||
})
|
||||
const nextActionLabel = computed(() => {
|
||||
const action = nextV4Action.value
|
||||
if (!action) {
|
||||
return '-'
|
||||
}
|
||||
if (action.action_type === 'REVIEW') {
|
||||
return t('order.nextStepReview')
|
||||
}
|
||||
if (action.action_type === 'CONFIRM') {
|
||||
return t('order.nextStepConfirm')
|
||||
}
|
||||
return t('order.nextActionNone')
|
||||
})
|
||||
|
||||
watch(
|
||||
orderId,
|
||||
@@ -458,27 +448,30 @@ function formatRoomSummary(room: ReservationOrderV4RoomSummary): string {
|
||||
return `${displayValue(room.room_type_code)} × ${room.room_count ?? '-'}`
|
||||
}
|
||||
|
||||
function formatNextActionType(actionType: ReservationOrderV4NextActionResult['action_type']): string {
|
||||
if (actionType === 'CONFIRM') {
|
||||
return t('order.nextActionConfirm')
|
||||
}
|
||||
if (actionType === 'REVIEW') {
|
||||
return t('order.nextActionReview')
|
||||
}
|
||||
if (actionType === 'NONE') {
|
||||
return t('order.nextActionNone')
|
||||
}
|
||||
return actionType ?? '-'
|
||||
}
|
||||
|
||||
function v4OrderReference(item: ReservationV4OrderTaskTimelineItem): string {
|
||||
return item.order_ref?.trim() || item.source_message_summary?.subject?.trim() || `#${item.order_task_id}`
|
||||
return item.source_message_summary?.subject?.trim() || t('order.processingRecord')
|
||||
}
|
||||
|
||||
function cardTimelineTitle(card: ReservationOrderV4TaskCardTimelineItem): string {
|
||||
const title = formatReservationTaskCard(t, card.card_type, card.card_type)
|
||||
const index = card.source_event_index == null ? '' : ` · ${t('order.sourceEventIndex', { index: card.source_event_index })}`
|
||||
return `${title}${index}`
|
||||
if (card.card_type === 'SOURCE_MESSAGE_DISPLAY') {
|
||||
return t('taskV4.sourceMessageCard')
|
||||
}
|
||||
if (card.card_type === 'BASIC_INFORMATION') {
|
||||
return t('taskV4.basicInformationCard')
|
||||
}
|
||||
if (card.card_type === 'ROOM_INFORMATION') {
|
||||
return t('taskV4.roomInformationCard')
|
||||
}
|
||||
if (card.card_type === 'PAYMENT') {
|
||||
return t('taskV4.paymentCard')
|
||||
}
|
||||
if (card.card_type === 'ROOMING_LIST') {
|
||||
return t('taskV4.roomingList.title')
|
||||
}
|
||||
if (card.card_type === 'TRACE_RESERVATION_NOTES') {
|
||||
return t('taskV4.trace.title')
|
||||
}
|
||||
return formatReservationTaskCard(t, card.card_type, card.card_type)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -558,7 +551,7 @@ function cardTimelineTitle(card: ReservationOrderV4TaskCardTimelineItem): string
|
||||
}
|
||||
|
||||
.card-summary dl {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.order-summary dt,
|
||||
@@ -662,6 +655,12 @@ function cardTimelineTitle(card: ReservationOrderV4TaskCardTimelineItem): string
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card-summary-list h3 {
|
||||
margin: 0;
|
||||
color: var(--th-color-slate-700);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.source-item,
|
||||
.v4-timeline-item,
|
||||
.card-summary {
|
||||
|
||||
@@ -32,43 +32,17 @@
|
||||
>
|
||||
</label>
|
||||
<label>
|
||||
<span>{{ t('taskList.itemType') }}</span>
|
||||
<span>{{ t('taskList.businessStatus') }}</span>
|
||||
<select
|
||||
v-model="filters.item_type"
|
||||
name="item_type"
|
||||
@change="changeItemType"
|
||||
v-model="filters.business_status"
|
||||
name="business_status"
|
||||
@change="changeBusinessStatus"
|
||||
>
|
||||
<option value="">{{ t('common.all') }}</option>
|
||||
<option value="ORDER_TASK">{{ t('taskList.itemTypeOrderTask') }}</option>
|
||||
<option value="SOURCE_NOTIFICATION">{{ t('taskList.itemTypeSourceNotification') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>{{ t('taskList.orderTaskStatus') }}</span>
|
||||
<select
|
||||
v-model="filters.order_task_status"
|
||||
name="order_task_status"
|
||||
:disabled="!isOrderTaskMode"
|
||||
@change="resetToFirstPage"
|
||||
>
|
||||
<option value="">{{ t('common.all') }}</option>
|
||||
<option value="OPEN">{{ t('status.OPEN') }}</option>
|
||||
<option value="COMPLETED">{{ t('status.COMPLETED') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>{{ t('taskList.cardStatus') }}</span>
|
||||
<select
|
||||
v-model="filters.card_status"
|
||||
name="card_status"
|
||||
:disabled="!isOrderTaskMode"
|
||||
@change="resetToFirstPage"
|
||||
>
|
||||
<option value="">{{ t('common.all') }}</option>
|
||||
<option value="PENDING_CONFIRM">{{ t('status.PENDING_CONFIRM') }}</option>
|
||||
<option value="REVIEW_REQUIRED">{{ t('status.REVIEW_REQUIRED') }}</option>
|
||||
<option value="CONFIRMED">{{ t('status.CONFIRMED') }}</option>
|
||||
<option value="READONLY">{{ t('status.READONLY') }}</option>
|
||||
<option value="PENDING_CONFIRM">{{ t('taskList.filterPending') }}</option>
|
||||
<option value="REVIEW_REQUIRED">{{ t('taskList.filterReview') }}</option>
|
||||
<option value="COMPLETED">{{ t('taskList.filterCompleted') }}</option>
|
||||
<option value="SOURCE_NOTIFICATION">{{ t('taskList.filterSourceNotification') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
@@ -102,7 +76,7 @@
|
||||
v-else-if="!items.length"
|
||||
class="empty-state"
|
||||
>
|
||||
{{ t('common.noData') }}
|
||||
{{ emptyStateText }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -127,15 +101,10 @@
|
||||
>
|
||||
<td>
|
||||
<strong>{{ itemKindLabel(item) }}</strong>
|
||||
<small class="th-code">#{{ item.target_id }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="item.item_type === 'ORDER_TASK'">
|
||||
<strong>{{ item.display_order_key ?? t('taskList.orderUnresolved') }}</strong>
|
||||
<small
|
||||
v-if="item.order_id"
|
||||
class="th-code"
|
||||
>{{ item.order_id }}</small>
|
||||
</template>
|
||||
<template v-else>
|
||||
<strong>{{ t('taskList.sourceNotificationNoOrder') }}</strong>
|
||||
@@ -256,14 +225,13 @@ import type {
|
||||
ReservationV4OrderTaskListItem,
|
||||
ReservationV4WorkbenchFilters,
|
||||
ReservationV4WorkbenchItem,
|
||||
ReservationV4WorkbenchItemType,
|
||||
} from '@/types/reservation'
|
||||
import {
|
||||
formatReservationReadonlyReason,
|
||||
} from '@/utils/reservationDisplay'
|
||||
import { formatReservationDateTime } from '@/utils/reservationFormat'
|
||||
|
||||
type WorkbenchItemFilter = ReservationV4WorkbenchItemType | ''
|
||||
type BusinessStatusFilter = '' | 'PENDING_CONFIRM' | 'REVIEW_REQUIRED' | 'COMPLETED' | 'SOURCE_NOTIFICATION'
|
||||
type WorkbenchRow = ReservationV4WorkbenchItem & {
|
||||
order_id?: string | null
|
||||
}
|
||||
@@ -277,21 +245,36 @@ const page = ref(createPage(1, 20, 0))
|
||||
const total = computed(() => page.value.total)
|
||||
const currentPage = computed(() => page.value.page_num)
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(page.value.total / Math.max(page.value.page_size, 1))))
|
||||
const isOrderTaskMode = computed(() => filters.item_type === 'ORDER_TASK')
|
||||
const emptyStateText = computed(() => {
|
||||
if (filters.business_status === 'PENDING_CONFIRM') {
|
||||
return t('taskList.noPendingItems')
|
||||
}
|
||||
if (filters.business_status === 'REVIEW_REQUIRED') {
|
||||
return t('taskList.noReviewItems')
|
||||
}
|
||||
if (filters.business_status === 'COMPLETED') {
|
||||
return t('taskList.noCompletedItems')
|
||||
}
|
||||
if (filters.business_status === 'SOURCE_NOTIFICATION') {
|
||||
return t('taskList.noSourceNotifications')
|
||||
}
|
||||
return t('taskList.noOpenItems')
|
||||
})
|
||||
const isOrderTaskListMode = computed(() =>
|
||||
filters.business_status === 'PENDING_CONFIRM' ||
|
||||
filters.business_status === 'REVIEW_REQUIRED' ||
|
||||
filters.business_status === 'COMPLETED',
|
||||
)
|
||||
let taskListRequestSequence = 0
|
||||
|
||||
const filters = reactive<{
|
||||
keyword: string
|
||||
item_type: WorkbenchItemFilter
|
||||
order_task_status: '' | 'OPEN' | 'COMPLETED'
|
||||
card_status: '' | 'READONLY' | 'PENDING_CONFIRM' | 'REVIEW_REQUIRED' | 'CONFIRMED'
|
||||
business_status: BusinessStatusFilter
|
||||
page_num: number
|
||||
page_size: number
|
||||
}>({
|
||||
keyword: '',
|
||||
item_type: '',
|
||||
order_task_status: '',
|
||||
card_status: '',
|
||||
business_status: '',
|
||||
page_num: 1,
|
||||
page_size: 20,
|
||||
})
|
||||
@@ -319,7 +302,7 @@ async function loadTasks(): Promise<void> {
|
||||
loading.value = true
|
||||
errorMessage.value = ''
|
||||
try {
|
||||
const result = isOrderTaskMode.value
|
||||
const result = isOrderTaskListMode.value
|
||||
? await fetchReservationV4OrderTasks(buildOrderTaskFilters())
|
||||
: await fetchReservationV4WorkbenchItems(buildWorkbenchFilters())
|
||||
if (requestSequence !== taskListRequestSequence) {
|
||||
@@ -344,7 +327,7 @@ async function loadTasks(): Promise<void> {
|
||||
function buildWorkbenchFilters(): ReservationV4WorkbenchFilters {
|
||||
return {
|
||||
keyword: filters.keyword,
|
||||
item_type: filters.item_type,
|
||||
item_type: filters.business_status === 'SOURCE_NOTIFICATION' ? 'SOURCE_NOTIFICATION' : '',
|
||||
page_num: filters.page_num,
|
||||
page_size: filters.page_size,
|
||||
}
|
||||
@@ -353,8 +336,10 @@ function buildWorkbenchFilters(): ReservationV4WorkbenchFilters {
|
||||
function buildOrderTaskFilters(): ReservationV4OrderTaskListFilters {
|
||||
return {
|
||||
keyword: filters.keyword,
|
||||
order_task_status: filters.order_task_status,
|
||||
card_status: filters.card_status,
|
||||
order_task_status: filters.business_status === 'COMPLETED' ? 'COMPLETED' : '',
|
||||
card_status: filters.business_status === 'PENDING_CONFIRM' || filters.business_status === 'REVIEW_REQUIRED'
|
||||
? filters.business_status
|
||||
: '',
|
||||
page_num: filters.page_num,
|
||||
page_size: filters.page_size,
|
||||
}
|
||||
@@ -383,18 +368,12 @@ function toWorkbenchRowFromOrderTask(item: ReservationV4OrderTaskListItem): Work
|
||||
|
||||
function resetFilters(): void {
|
||||
filters.keyword = ''
|
||||
filters.item_type = ''
|
||||
filters.order_task_status = ''
|
||||
filters.card_status = ''
|
||||
filters.business_status = ''
|
||||
filters.page_num = 1
|
||||
filters.page_size = 20
|
||||
}
|
||||
|
||||
function changeItemType(): void {
|
||||
if (!isOrderTaskMode.value) {
|
||||
filters.order_task_status = ''
|
||||
filters.card_status = ''
|
||||
}
|
||||
function changeBusinessStatus(): void {
|
||||
resetToFirstPage()
|
||||
}
|
||||
|
||||
@@ -440,7 +419,7 @@ function sourceSubject(item: WorkbenchRow): string {
|
||||
}
|
||||
|
||||
function sourceSender(item: WorkbenchRow): string {
|
||||
return item.source_message_summary?.sender_summary ?? item.source_message_id ?? '-'
|
||||
return item.source_message_summary?.sender_summary ?? '-'
|
||||
}
|
||||
|
||||
function sourceReceivedAt(item: WorkbenchRow): string | null {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
{{ t('taskV4.orderTaskSummary') }}
|
||||
</h2>
|
||||
<p class="th-muted">
|
||||
#{{ detail.order_task.order_task_id }}
|
||||
{{ detail.source_message_summary?.subject ?? t('taskList.noSourceSubject') }}
|
||||
</p>
|
||||
</div>
|
||||
<ReservationStatusBadge :status="detail.order_task.order_task_status" />
|
||||
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ t('taskV4.targetBookingType') }}</span>
|
||||
<strong>{{ detail.order_task.target_booking_type ?? '-' }}</strong>
|
||||
<strong>{{ bookingTypeLabel }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ t('taskV4.targetLocator') }}</span>
|
||||
@@ -56,7 +56,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ t('taskV4.targetResolutionStatus') }}</span>
|
||||
<strong>{{ detail.order_task.target_resolution_status ?? '-' }}</strong>
|
||||
<strong>{{ targetResolutionLabel }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{ t('taskV4.cardCounts') }}</span>
|
||||
@@ -87,7 +87,6 @@
|
||||
:editable-keys="editableFieldKeys(detail.basic_information_card)"
|
||||
:validation-errors="cardFieldErrors[detail.basic_information_card.card_id] ?? {}"
|
||||
:hotel-id="detail.order_task.hotel_id"
|
||||
:order-task-id="detail.order_task.order_task_id"
|
||||
:source-message-id="detail.order_task.source_message_id"
|
||||
:order-reference="displayOrderReference"
|
||||
:target-locator="targetLocator"
|
||||
@@ -133,7 +132,6 @@
|
||||
:editable-keys="editableFieldKeys(businessCard)"
|
||||
:validation-errors="cardFieldErrors[businessCard.card_id] ?? {}"
|
||||
:hotel-id="detail.order_task.hotel_id"
|
||||
:order-task-id="detail.order_task.order_task_id"
|
||||
:source-message-id="detail.order_task.source_message_id"
|
||||
:order-reference="displayOrderReference"
|
||||
:target-locator="targetLocator"
|
||||
@@ -153,33 +151,6 @@
|
||||
</template>
|
||||
</section>
|
||||
|
||||
<section class="th-section diagnostics-section">
|
||||
<div class="th-section-header">
|
||||
<div>
|
||||
<h2 class="th-section-title">
|
||||
{{ t('taskV4.adapterContractErrors') }}
|
||||
</h2>
|
||||
<p class="th-muted">
|
||||
{{ t('taskV4.noAdapterContractErrors') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="detail.adapter_contract_errors.length"
|
||||
class="diagnostic-list"
|
||||
>
|
||||
<article
|
||||
v-for="error in detail.adapter_contract_errors"
|
||||
:key="error.transition_id ?? `${error.route_code}-${error.adapter_error_code}`"
|
||||
class="diagnostic-item"
|
||||
>
|
||||
<strong>{{ error.adapter_error_code ?? t('resultType.ADAPTER_CONTRACT_ERROR') }}</strong>
|
||||
<p>{{ error.adapter_error_message ?? '-' }}</p>
|
||||
<small>{{ formatTransitionSummary(error) }}</small>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ReservationV4SourceMessageCard
|
||||
:title="t('taskV4.sourceMessageCard')"
|
||||
:source-message-summary="detail.source_message_summary"
|
||||
@@ -205,15 +176,12 @@ import {
|
||||
} from '@/services/reservationService'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
import type {
|
||||
ReservationAiTransitionDisplayResult,
|
||||
ReservationV4CardConfirmRequest,
|
||||
ReservationRecord,
|
||||
ReservationV4OrderTaskDetailResult,
|
||||
ReservationV4TaskCardResult,
|
||||
} from '@/types/reservation'
|
||||
import {
|
||||
formatReservationResultType,
|
||||
formatReservationRouteCode,
|
||||
formatReservationTaskCard,
|
||||
} from '@/utils/reservationDisplay'
|
||||
import { formatReservationDateTime } from '@/utils/reservationFormat'
|
||||
@@ -247,14 +215,34 @@ let requestSequence = 0
|
||||
const orderTaskId = computed(() => String(route.params.orderTaskId ?? ''))
|
||||
const displayOrderReference = computed(() => {
|
||||
const orderTask = detail.value?.order_task
|
||||
return orderTask?.display_order_key ?? orderTask?.order_ref ?? orderTask?.order_id ?? '-'
|
||||
return orderTask?.display_order_key?.trim() || orderTask?.target_locator_value?.trim() || '-'
|
||||
})
|
||||
const targetLocator = computed(() => {
|
||||
const orderTask = detail.value?.order_task
|
||||
if (!orderTask) {
|
||||
return '-'
|
||||
}
|
||||
return [orderTask.target_locator_type, orderTask.target_locator_value].filter(Boolean).join(': ') || '-'
|
||||
return orderTask.target_locator_value?.trim() || orderTask.display_order_key?.trim() || '-'
|
||||
})
|
||||
const bookingTypeLabel = computed(() => {
|
||||
const value = detail.value?.order_task.target_booking_type?.toUpperCase()
|
||||
if (value === 'GROUP') {
|
||||
return t('taskV4.bookingTypeGroup')
|
||||
}
|
||||
if (value === 'FIT') {
|
||||
return t('taskV4.bookingTypeFit')
|
||||
}
|
||||
return value ? t('taskV4.bookingTypeUnknown') : '-'
|
||||
})
|
||||
const targetResolutionLabel = computed(() => {
|
||||
const value = detail.value?.order_task.target_resolution_status?.toUpperCase()
|
||||
if (value === 'RESOLVED') {
|
||||
return t('taskV4.targetResolved')
|
||||
}
|
||||
if (value === 'UNRESOLVED') {
|
||||
return t('taskV4.targetUnresolved')
|
||||
}
|
||||
return value ? t('taskV4.targetPending') : '-'
|
||||
})
|
||||
const hasConfirmPermission = computed(() => authStore.hasPermission('RESERVATION_TASK_CONFIRM'))
|
||||
const hasReviewPermission = computed(() => authStore.hasPermission('RESERVATION_MANUAL_REVIEW_RESOLVE'))
|
||||
@@ -544,6 +532,12 @@ function applyCardActionError(card: ReservationV4TaskCardResult, error: unknown,
|
||||
}
|
||||
|
||||
function cardTitle(card: ReservationV4TaskCardResult): string {
|
||||
if (card.card_type === 'ROOM_INFORMATION') {
|
||||
return t('taskV4.roomInformationCard')
|
||||
}
|
||||
if (isPaymentCard(card)) {
|
||||
return t('taskV4.paymentCard')
|
||||
}
|
||||
if (isRoomingListCard(card)) {
|
||||
return t('taskV4.roomingList.title')
|
||||
}
|
||||
@@ -553,14 +547,6 @@ function cardTitle(card: ReservationV4TaskCardResult): string {
|
||||
return formatReservationTaskCard(t, card.card_type)
|
||||
}
|
||||
|
||||
function formatTransitionSummary(error: ReservationAiTransitionDisplayResult): string {
|
||||
return [
|
||||
error.route_code ? formatReservationRouteCode(t, error.route_code) : '',
|
||||
error.result_type ? formatReservationResultType(t, error.result_type) : '',
|
||||
error.transition_id ? `#${error.transition_id}` : '',
|
||||
].filter(Boolean).join(' · ')
|
||||
}
|
||||
|
||||
function formatApiError(error: unknown, fallback: string): {
|
||||
message: string
|
||||
details: unknown
|
||||
|
||||
Reference in New Issue
Block a user