hotelId?: string
- orderTaskId?: string | null
sourceMessageId?: string | null
orderReference?: string | null
targetLocator?: string | null
@@ -229,7 +181,6 @@ const props = withDefaults(defineProps<{
}>(), {
validationErrors: () => ({}),
hotelId: undefined,
- orderTaskId: null,
sourceMessageId: null,
orderReference: null,
targetLocator: null,
@@ -267,22 +218,17 @@ const availabilityReason = computed(() => {
? formatReservationReadonlyReason(t, props.card.availability.readonly_reason_code)
: '')
})
-
-const safePayloadRows = computed(() => {
- if (!props.card.display_payload) {
- return []
- }
- return Object.entries(props.card.display_payload)
- .filter(([key]) => !isReservationV4UnsafeDisplayKey(key))
- .slice(0, 12)
- .map(([key, value]) => ({
- key,
- value: stringifyReservationV4SafeDisplayValue(
- value,
- t('taskV4.field.empty'),
- t('taskV4.field.hidden'),
- ),
- }))
+const showPrimaryAction = computed(() =>
+ props.card.card_status === 'PENDING_CONFIRM' || props.card.card_status === 'REVIEW_REQUIRED',
+)
+const primaryActionDisabled = computed(() =>
+ props.card.card_status === 'REVIEW_REQUIRED' ? !props.canReview : !props.canConfirm,
+)
+const showOrderLink = computed(() => Boolean(props.boundOrderId && !isRoomingListCard.value))
+const cardNotices = computed(() => {
+ return [
+ availabilityReason.value,
+ ].filter((notice): notice is string => Boolean(notice?.trim()))
})
function updateReviewForm(field: keyof ReviewForm, event: Event): void {
@@ -297,6 +243,14 @@ function isPaymentAttachmentIdField(field: ReservationV4TaskCardResult['fields']
const normalizedKey = `${field.field_pointer || ''} ${field.field_path || ''}`.toLowerCase()
return normalizedKey.includes('attachment_ids')
}
+
+function handlePrimaryAction(): void {
+ if (props.card.card_status === 'REVIEW_REQUIRED') {
+ emit('resolveReview')
+ return
+ }
+ emit('confirm')
+}
diff --git a/client/src/i18n/locales/en-US.ts b/client/src/i18n/locales/en-US.ts
index 75c57d3..9d9e1ed 100644
--- a/client/src/i18n/locales/en-US.ts
+++ b/client/src/i18n/locales/en-US.ts
@@ -452,12 +452,12 @@ export default {
order: {
summary: 'Order summary',
taskQueue: 'Order task queue',
- v4TaskTimeline: 'V4 order task timeline',
- v4TaskTimelineEmpty: 'No V4 order tasks.',
- v4TaskTimelineHint: 'Shows safe order-task and card summaries only. Complete actions in the V4 task detail page.',
- v4OverviewTitle: 'Current V4 confirmed snapshot',
- v4OverviewSubtitle: 'Derived from confirmed cards only, not from unconfirmed AI suggestions.',
- v4OverviewEmpty: 'No confirmed V4 order snapshot yet.',
+ v4TaskTimeline: 'Processing records',
+ v4TaskTimelineEmpty: 'No processing records.',
+ v4TaskTimelineHint: 'Shows source-email processing records and item status summaries only. Complete actions in the reservation item page.',
+ v4OverviewTitle: 'Order information overview',
+ v4OverviewSubtitle: 'Derived from confirmed items only, not from unconfirmed suggestions.',
+ v4OverviewEmpty: 'No confirmed order information yet.',
accountCode: 'Account Code',
accountName: 'Account Name',
marketCode: 'Market Code',
@@ -466,31 +466,36 @@ export default {
rateCode: 'Rate Code',
roomItems: 'Confirmed room items',
latestConfirmedAt: 'Latest confirmed at',
- traceCardStatus: 'Trace card status',
- roomingListCardStatus: 'Rooming List card status',
- paymentCardStatus: 'Payment card status',
+ traceCardStatus: 'Follow-up item status',
+ roomingListCardStatus: 'Rooming list item status',
+ paymentCardStatus: 'Payment voucher status',
nextV4ActionTitle: 'Next action',
- nextV4ActionSubtitle: 'The order overview only links to the task page; it does not confirm, review, or edit cards.',
- nextV4ActionType: 'Action type',
- nextV4ActionStatus: 'Action status',
- nextV4CardId: 'Card ID',
- openOrderTaskCount: 'Open V4 tasks',
- enterV4Task: 'Open task page',
- nextActionConfirm: 'Confirm card',
- nextActionReview: 'Manual review',
- nextActionNone: 'No pending action',
- nextV4ActionEmpty: 'No next V4 action.',
- relatedSourceMessages: 'Related source messages',
+ nextV4ActionSubtitle: 'The order overview only links to the handling page; it does not confirm, review, or edit items.',
+ nextV4ActionType: 'Next step',
+ nextV4ActionStatus: 'Handling status',
+ nextV4CardId: 'Item ID',
+ openOrderTaskCount: 'Open items',
+ enterV4Task: 'Open handling page',
+ nextActionConfirm: 'Confirm item',
+ nextActionReview: 'Needs review',
+ nextActionNone: 'No pending item',
+ nextStep: 'Reservation item',
+ nextStepConfirm: 'An item needs confirmation',
+ nextStepReview: 'An item needs review',
+ nextV4ActionEmpty: 'No next reservation item.',
+ relatedSourceMessages: 'Related source emails',
relatedSourceMessagesHint: 'Shows headers and conversation summary only. Open the conversation page for full email content.',
- relatedSourceMessagesEmpty: 'No related source messages.',
+ relatedSourceMessagesEmpty: 'No related source emails.',
externalConversationId: 'External conversation ID',
latestActivityAt: 'Latest activity',
- v4CardsEmpty: 'No card summaries.',
- cardId: 'Card ID',
+ v4CardsEmpty: 'No item status summaries.',
+ cardStatusSummary: 'Item status summary',
+ cardId: 'Item ID',
reviewStatus: 'Review status',
confirmedBy: 'Confirmed by',
confirmedAt: 'Confirmed at',
- sourceEventIndex: 'Source event {index}',
+ sourceEventIndex: 'Source email event {index}',
+ processingRecord: 'Source email processing record',
sourceMessage: 'Source message',
businessKeySource: 'Business key source',
updatedAt: 'Last updated',
@@ -508,25 +513,35 @@ export default {
sourceSummaryPending: 'Source email summary fields for the order task timeline are pending backend support. Open a task for available source details.',
},
taskList: {
- title: 'Task list',
- subtitle: 'Review Reservation work by task queue',
+ title: 'Pending reservation items',
+ subtitle: 'Review reservation items that need confirmation, review, or acknowledgement',
keyword: 'Order / email keyword',
keywordPlaceholder: 'Order no. / email keyword',
taskType: 'Task type',
taskSubtype: 'Task subtype',
- tableTitle: 'Task data',
- totalSuffix: 'tasks',
+ tableTitle: 'Reservation items',
+ totalSuffix: 'items',
noSourceSubject: 'No source subject',
sourceReceivedAt: 'Source time',
+ businessStatus: 'Item status',
+ filterPending: 'Pending',
+ filterReview: 'Needs review',
+ filterCompleted: 'Completed',
+ filterSourceNotification: 'Source notification',
+ noOpenItems: 'No reservation items need handling',
+ noPendingItems: 'No reservation items are pending confirmation',
+ noReviewItems: 'No reservation items need review',
+ noCompletedItems: 'No completed reservation items',
+ noSourceNotifications: 'No source notifications',
itemType: 'Work item type',
- itemTypeOrderTask: 'Order task',
+ itemTypeOrderTask: 'Reservation item',
itemTypeSourceNotification: 'Source notification',
orderTaskStatus: 'Order task status',
cardStatus: 'Card status',
orderUnresolved: 'Order ownership pending',
sourceNotificationNoOrder: 'No linked order',
- sourceNotificationNoOrderHint: 'S10/S99 read-only source notification',
- cardCountsTotal: '{total} cards',
+ sourceNotificationNoOrderHint: 'This email is not linked to an order. Review the email content.',
+ cardCountsTotal: '{total} items',
cardCountsPending: 'Pending {count}',
cardCountsReview: 'Review {count}',
cardCountsConfirmed: 'Confirmed {count}',
@@ -537,42 +552,50 @@ export default {
queueAll: 'All queue states',
queueIncluded: 'In queue',
queueExcluded: 'Not in queue',
- viewTask: 'View task',
+ viewTask: 'Handle item',
viewOrder: 'View order',
viewConversation: 'View email conversation',
sourceOnlyOrderLabel: 'Source message only',
hiddenTechnicalOrder: 'No visible order',
columns: {
- item: 'Work item',
+ item: 'Item type',
task: 'Task',
order: 'Order',
- card: 'Task card',
+ card: 'Item',
status: 'Status',
source: 'Source email',
- cards: 'Cards',
+ cards: 'Item progress',
queue: 'Queue',
},
},
taskV4: {
- title: 'V4 order task detail',
- subtitle: 'Handle the current order task with the backend V4 task card model.',
- orderTaskSummary: 'Order task summary',
- orderTaskId: 'Order task ID',
+ title: 'Reservation item handling',
+ subtitle: 'Handle the current reservation item from the source email.',
+ orderTaskSummary: 'Item summary',
+ orderTaskId: 'Item ID',
orderReference: 'Order reference',
- targetBookingType: 'Target booking type',
- targetLocator: 'Target locator',
- targetResolutionStatus: 'Target resolution status',
- cardCounts: 'Card counts',
- sourceMessageCard: 'Source email card',
- basicInformationCard: 'Basic information card',
- businessCards: 'Business task cards',
+ targetBookingType: 'Booking type',
+ targetLocator: 'Order clue',
+ targetResolutionStatus: 'Order ownership',
+ cardCounts: 'Item counts',
+ sourceMessageCard: 'Source email',
+ basicInformationCard: 'Reservation basics',
+ roomInformationCard: 'Room and dates',
+ paymentCard: 'Payment voucher',
+ businessCards: 'Reservation items',
adapterContractErrors: 'Adapter contract diagnostics',
noAdapterContractErrors: 'No adapter contract errors.',
- noBusinessCards: 'No business task cards.',
+ noBusinessCards: 'No reservation items to handle.',
displayPayload: 'Display summary',
rawCardPayload: 'Safe card summary',
readonlyReason: 'Read-only reason',
blockedReason: 'Blocked reason',
+ bookingTypeGroup: 'Group booking',
+ bookingTypeFit: 'FIT booking',
+ bookingTypeUnknown: 'Booking type pending',
+ targetResolved: 'Confirmed',
+ targetUnresolved: 'Pending confirmation',
+ targetPending: 'Pending confirmation',
confirmCard: 'Confirm card',
confirmingCard: 'Confirming',
resolveReview: 'Submit review',
@@ -624,14 +647,14 @@ export default {
fileMeta: '{type} · {size}',
},
roomingList: {
- title: 'Rooming List action',
+ title: 'Rooming list item',
targetOrder: 'Target order',
unboundOrder: 'No visible order is bound yet',
description: 'The current source message contains a Rooming List item that needs manual handling.',
confirmHint: 'This first version only confirms that the item has been handled manually. Guest details and attachment content are not displayed or processed here.',
},
trace: {
- title: 'Trace notes',
+ title: 'Follow-up item',
itemLabel: 'Item {index}',
generalTitle: 'General note',
extraBedTitle: 'Extra bed note',
@@ -643,7 +666,6 @@ export default {
stale: 'This catalog is a stale snapshot. You can still select from it; backend validation remains final.',
warning: 'Catalog note: {warning}',
error: 'Catalog is temporarily unavailable. Refresh and retry.',
- catalogMeta: 'Catalog source: {source}, version: {version}',
accountDerived: 'Market: {market} / Source: {source} will be shown from the catalog; backend confirmation remains final.',
currentNotInCatalog: 'Original value "{value}" is not in the current catalog. Select again.',
},
@@ -655,6 +677,7 @@ export default {
attachments: 'Attachments',
excerpt: 'Email excerpt',
body: 'Current email body',
+ bodyCollapsed: 'The email body is folded. Expand to read the current trigger email.',
bodyLoading: 'Loading current email body...',
bodyLoadFailed: 'Current email body cannot be loaded right now. Open the email conversation to view it.',
noBody: 'No current email body.',
@@ -671,7 +694,7 @@ export default {
selectPlaceholder: 'Select',
},
error: {
- loadFailed: 'Failed to load V4 order task detail.',
+ loadFailed: 'Failed to load the reservation item handling page.',
actionFailed: 'Action failed. Check backend response details.',
},
},
diff --git a/client/src/i18n/locales/th-TH.ts b/client/src/i18n/locales/th-TH.ts
index a7d3989..fef3b6d 100644
--- a/client/src/i18n/locales/th-TH.ts
+++ b/client/src/i18n/locales/th-TH.ts
@@ -452,12 +452,12 @@ export default {
order: {
summary: 'สรุปออเดอร์',
taskQueue: 'คิวงานของออเดอร์นี้',
- v4TaskTimeline: 'ไทม์ไลน์งานออเดอร์ V4',
- v4TaskTimelineEmpty: 'ยังไม่มีงานออเดอร์ V4',
- v4TaskTimelineHint: 'แสดงเฉพาะสรุปงานและการ์ดที่ปลอดภัย หากต้องดำเนินการให้เข้าไปที่หน้ารายละเอียดงาน V4',
- v4OverviewTitle: 'สแนปช็อต V4 ที่ยืนยันแล้ว',
- v4OverviewSubtitle: 'มาจากการ์ดที่ยืนยันแล้วเท่านั้น ไม่ใช้คำแนะนำ AI ที่ยังไม่ยืนยัน',
- v4OverviewEmpty: 'ยังไม่มีสแนปช็อตออเดอร์ V4 ที่ยืนยันแล้ว',
+ v4TaskTimeline: 'ประวัติการดำเนินการ',
+ v4TaskTimelineEmpty: 'ยังไม่มีประวัติการดำเนินการ',
+ v4TaskTimelineHint: 'แสดงเฉพาะประวัติการประมวลผลอีเมลต้นทางและสรุปสถานะรายการ หากต้องดำเนินการให้เข้าไปที่หน้าจัดการรายการจอง',
+ v4OverviewTitle: 'ภาพรวมข้อมูลออเดอร์',
+ v4OverviewSubtitle: 'มาจากรายการที่ยืนยันแล้วเท่านั้น ไม่ใช้ข้อมูลแนะนำที่ยังไม่ยืนยัน',
+ v4OverviewEmpty: 'ยังไม่มีข้อมูลออเดอร์ที่ยืนยันแล้ว',
accountCode: 'Account Code',
accountName: 'Account Name',
marketCode: 'Market Code',
@@ -466,31 +466,36 @@ export default {
rateCode: 'Rate Code',
roomItems: 'ประเภทห้องและจำนวนที่ยืนยันแล้ว',
latestConfirmedAt: 'เวลายืนยันล่าสุด',
- traceCardStatus: 'สถานะการ์ด Trace',
- roomingListCardStatus: 'สถานะการ์ด Rooming List',
- paymentCardStatus: 'สถานะการ์ด Payment',
+ traceCardStatus: 'สถานะรายการติดตาม',
+ roomingListCardStatus: 'สถานะรายการ Rooming List',
+ paymentCardStatus: 'สถานะหลักฐานการชำระเงิน',
nextV4ActionTitle: 'ขั้นตอนถัดไป',
- nextV4ActionSubtitle: 'หน้าสรุปออเดอร์เป็นทางเข้าเท่านั้น ไม่ยืนยัน ตรวจสอบ หรือแก้ไขการ์ดในหน้านี้',
- nextV4ActionType: 'ประเภทการดำเนินการ',
- nextV4ActionStatus: 'สถานะการดำเนินการ',
- nextV4CardId: 'รหัสการ์ด',
- openOrderTaskCount: 'งาน V4 ที่ยังไม่เสร็จ',
- enterV4Task: 'ไปหน้าดำเนินการ',
- nextActionConfirm: 'ยืนยันการ์ด',
- nextActionReview: 'ตรวจสอบด้วยคน',
- nextActionNone: 'ไม่มีงานรอดำเนินการ',
- nextV4ActionEmpty: 'ยังไม่มีขั้นตอน V4 ถัดไป',
- relatedSourceMessages: 'ข้อความต้นทางที่เกี่ยวข้อง',
+ nextV4ActionSubtitle: 'หน้าสรุปออเดอร์เป็นทางเข้าเท่านั้น ไม่ยืนยัน ตรวจสอบ หรือแก้ไขรายการในหน้านี้',
+ nextV4ActionType: 'ขั้นตอนถัดไป',
+ nextV4ActionStatus: 'สถานะการจัดการ',
+ nextV4CardId: 'รหัสรายการ',
+ openOrderTaskCount: 'รายการที่ยังต้องจัดการ',
+ enterV4Task: 'ไปหน้าจัดการ',
+ nextActionConfirm: 'ยืนยันรายการ',
+ nextActionReview: 'ต้องตรวจสอบ',
+ nextActionNone: 'ไม่มีรายการรอดำเนินการ',
+ nextStep: 'รายการจอง',
+ nextStepConfirm: 'มีรายการรอยืนยัน',
+ nextStepReview: 'มีรายการต้องตรวจสอบ',
+ nextV4ActionEmpty: 'ยังไม่มีรายการจองถัดไป',
+ relatedSourceMessages: 'อีเมลต้นทางที่เกี่ยวข้อง',
relatedSourceMessagesHint: 'แสดงเฉพาะหัวอีเมลและสรุปเธรด หากต้องการอ่านทั้งหมดให้เปิดหน้าบทสนทนาอีเมล',
- relatedSourceMessagesEmpty: 'ยังไม่มีข้อความต้นทางที่เกี่ยวข้อง',
+ relatedSourceMessagesEmpty: 'ยังไม่มีอีเมลต้นทางที่เกี่ยวข้อง',
externalConversationId: 'รหัสเธรดภายนอก',
latestActivityAt: 'กิจกรรมล่าสุด',
- v4CardsEmpty: 'ยังไม่มีสรุปการ์ด',
- cardId: 'รหัสการ์ด',
+ v4CardsEmpty: 'ยังไม่มีสรุปสถานะรายการ',
+ cardStatusSummary: 'สรุปสถานะรายการ',
+ cardId: 'รหัสรายการ',
reviewStatus: 'สถานะตรวจสอบ',
confirmedBy: 'ผู้ยืนยัน',
confirmedAt: 'เวลายืนยัน',
- sourceEventIndex: 'เหตุการณ์ต้นทาง {index}',
+ sourceEventIndex: 'เหตุการณ์อีเมลต้นทาง {index}',
+ processingRecord: 'ประวัติการประมวลผลอีเมลต้นทาง',
sourceMessage: 'ข้อความต้นทาง',
businessKeySource: 'แหล่งที่มาของเลขธุรกิจ',
updatedAt: 'อัปเดตล่าสุด',
@@ -508,25 +513,35 @@ export default {
sourceSummaryPending: 'ข้อมูลสรุปอีเมลต้นทางในไทม์ไลน์งานยังรอหลังบ้านเพิ่มเติม โปรดเข้าไปดูจากงานที่เกี่ยวข้อง',
},
taskList: {
- title: 'รายการงาน',
- subtitle: 'ดูคิวงานการจองตามงาน',
+ title: 'รายการจองที่ต้องจัดการ',
+ subtitle: 'ดูรายการจองที่ต้องยืนยัน ตรวจสอบ หรือรับทราบ',
keyword: 'คำค้นออเดอร์ / อีเมล',
keywordPlaceholder: 'เลขออเดอร์ / คำค้นอีเมล',
taskType: 'ประเภทงาน',
taskSubtype: 'ประเภทย่อยของงาน',
- tableTitle: 'ข้อมูลงาน',
- totalSuffix: 'งาน',
+ tableTitle: 'รายการจอง',
+ totalSuffix: 'รายการ',
noSourceSubject: 'ไม่มีหัวข้ออีเมลต้นทาง',
sourceReceivedAt: 'เวลารับอีเมล',
+ businessStatus: 'สถานะรายการ',
+ filterPending: 'รอดำเนินการ',
+ filterReview: 'ต้องตรวจสอบ',
+ filterCompleted: 'เสร็จแล้ว',
+ filterSourceNotification: 'แจ้งเตือนต้นทาง',
+ noOpenItems: 'ยังไม่มีรายการจองที่ต้องจัดการ',
+ noPendingItems: 'ยังไม่มีรายการจองที่รอยืนยัน',
+ noReviewItems: 'ยังไม่มีรายการจองที่ต้องตรวจสอบ',
+ noCompletedItems: 'ยังไม่มีรายการจองที่เสร็จแล้ว',
+ noSourceNotifications: 'ยังไม่มีแจ้งเตือนต้นทาง',
itemType: 'ประเภทงานในคิว',
- itemTypeOrderTask: 'งานออเดอร์',
+ itemTypeOrderTask: 'รายการจอง',
itemTypeSourceNotification: 'แจ้งเตือนต้นทาง',
orderTaskStatus: 'สถานะงานออเดอร์',
cardStatus: 'สถานะการ์ด',
orderUnresolved: 'ยังรอยืนยันออเดอร์',
sourceNotificationNoOrder: 'ไม่ผูกกับออเดอร์',
- sourceNotificationNoOrderHint: 'แจ้งเตือนต้นทาง S10/S99 แบบอ่านอย่างเดียว',
- cardCountsTotal: 'รวม {total} การ์ด',
+ sourceNotificationNoOrderHint: 'อีเมลนี้ไม่ได้ผูกกับออเดอร์ โปรดตรวจสอบเนื้อหาอีเมล',
+ cardCountsTotal: 'รวม {total} รายการ',
cardCountsPending: 'รอยืนยัน {count}',
cardCountsReview: 'รอตรวจสอบ {count}',
cardCountsConfirmed: 'ยืนยันแล้ว {count}',
@@ -537,42 +552,50 @@ export default {
queueAll: 'สถานะคิวทั้งหมด',
queueIncluded: 'อยู่ในคิว',
queueExcluded: 'ไม่อยู่ในคิว',
- viewTask: 'ดูงาน',
+ viewTask: 'จัดการรายการ',
viewOrder: 'ดูออเดอร์',
viewConversation: 'ดูเธรดอีเมล',
sourceOnlyOrderLabel: 'ข้อความต้นทางแบบอ่านอย่างเดียว',
hiddenTechnicalOrder: 'ไม่มีออเดอร์ที่แสดงได้',
columns: {
- item: 'รายการงาน',
+ item: 'ประเภทรายการ',
task: 'งาน',
order: 'ออเดอร์',
- card: 'การ์ดงาน',
+ card: 'รายการ',
status: 'สถานะ',
source: 'อีเมลต้นทาง',
- cards: 'การ์ด',
+ cards: 'ความคืบหน้ารายการ',
queue: 'คิว',
},
},
taskV4: {
- title: 'รายละเอียดงานออเดอร์ V4',
- subtitle: 'จัดการงานออเดอร์ปัจจุบันตามโมเดลการ์ดงาน V4 จากหลังบ้าน',
- orderTaskSummary: 'สรุปงานออเดอร์',
- orderTaskId: 'รหัสงานออเดอร์',
+ title: 'จัดการรายการจอง',
+ subtitle: 'จัดการรายการจองปัจจุบันจากอีเมลต้นทาง',
+ orderTaskSummary: 'สรุปรายการ',
+ orderTaskId: 'รหัสรายการ',
orderReference: 'อ้างอิงออเดอร์',
- targetBookingType: 'ประเภทการจองเป้าหมาย',
- targetLocator: 'ข้อมูลระบุเป้าหมาย',
- targetResolutionStatus: 'สถานะการยืนยันเป้าหมาย',
- cardCounts: 'จำนวนการ์ด',
- sourceMessageCard: 'การ์ดอีเมลต้นทาง',
- basicInformationCard: 'การ์ดข้อมูลพื้นฐาน',
- businessCards: 'การ์ดงานธุรกิจ',
+ targetBookingType: 'ประเภทการจอง',
+ targetLocator: 'ข้อมูลช่วยระบุออเดอร์',
+ targetResolutionStatus: 'การผูกออเดอร์',
+ cardCounts: 'จำนวนรายการ',
+ sourceMessageCard: 'อีเมลต้นทาง',
+ basicInformationCard: 'ข้อมูลจองพื้นฐาน',
+ roomInformationCard: 'ห้องและวันที่',
+ paymentCard: 'หลักฐานการชำระเงิน',
+ businessCards: 'รายการจอง',
adapterContractErrors: 'ข้อมูลวินิจฉัยสัญญา Adapter',
noAdapterContractErrors: 'ไม่มีข้อผิดพลาดสัญญา Adapter',
- noBusinessCards: 'ยังไม่มีการ์ดงานธุรกิจ',
+ noBusinessCards: 'ยังไม่มีรายการจองที่ต้องจัดการ',
displayPayload: 'สรุปสำหรับแสดงผล',
rawCardPayload: 'สรุปการ์ดที่ปลอดภัย',
readonlyReason: 'เหตุผลที่แก้ไขไม่ได้',
blockedReason: 'เหตุผลที่ถูกบล็อก',
+ bookingTypeGroup: 'จองแบบกรุ๊ป',
+ bookingTypeFit: 'จองแบบ FIT',
+ bookingTypeUnknown: 'รอยืนยันประเภทการจอง',
+ targetResolved: 'ยืนยันแล้ว',
+ targetUnresolved: 'รอยืนยัน',
+ targetPending: 'รอยืนยัน',
confirmCard: 'ยืนยันการ์ด',
confirmingCard: 'กำลังยืนยัน',
resolveReview: 'ส่งผลตรวจสอบ',
@@ -624,14 +647,14 @@ export default {
fileMeta: '{type} · {size}',
},
roomingList: {
- title: 'งาน Rooming List',
+ title: 'รายการ Rooming List',
targetOrder: 'ออเดอร์เป้าหมาย',
unboundOrder: 'ยังไม่มีออเดอร์ที่มองเห็นได้ผูกอยู่',
description: 'ข้อความต้นทางปัจจุบันมีรายการ Rooming List ที่ต้องให้เจ้าหน้าที่จัดการ',
confirmHint: 'เวอร์ชันแรกนี้ใช้ยืนยันว่ารายการได้รับการจัดการด้วยคนแล้วเท่านั้น ไม่แสดงหรือประมวลผลรายชื่อแขกและเนื้อหาไฟล์แนบในหน้านี้',
},
trace: {
- title: 'หมายเหตุ Trace',
+ title: 'รายการติดตาม',
itemLabel: 'รายการ {index}',
generalTitle: 'หมายเหตุทั่วไป',
extraBedTitle: 'รายการเตียงเสริม',
@@ -643,7 +666,6 @@ export default {
stale: 'แค็ตตาล็อกนี้เป็น snapshot เก่า ยังเลือกได้ แต่การตรวจสอบฝั่งหลังบ้านเป็นผลสุดท้าย',
warning: 'หมายเหตุแค็ตตาล็อก: {warning}',
error: 'แค็ตตาล็อกไม่พร้อมใช้งานชั่วคราว โปรดรีเฟรชแล้วลองใหม่',
- catalogMeta: 'แหล่งที่มาแค็ตตาล็อก: {source}, เวอร์ชัน: {version}',
accountDerived: 'จะแสดง Market: {market} / Source: {source} จากแค็ตตาล็อก โดยผลยืนยันจากหลังบ้านเป็นผลสุดท้าย',
currentNotInCatalog: 'ค่าเดิม "{value}" ไม่อยู่ในแค็ตตาล็อกปัจจุบัน โปรดเลือกใหม่',
},
@@ -655,6 +677,7 @@ export default {
attachments: 'ไฟล์แนบ',
excerpt: 'ข้อความบางส่วนของอีเมล',
body: 'เนื้อหาอีเมลปัจจุบัน',
+ bodyCollapsed: 'เนื้อหาอีเมลถูกพับไว้ กดขยายเพื่ออ่านอีเมลต้นทางฉบับปัจจุบัน',
bodyLoading: 'กำลังโหลดเนื้อหาอีเมลปัจจุบัน...',
bodyLoadFailed: 'ยังโหลดเนื้อหาอีเมลปัจจุบันไม่ได้ โปรดเปิดเธรดอีเมลเพื่อดู',
noBody: 'ไม่มีเนื้อหาอีเมลปัจจุบัน',
@@ -671,7 +694,7 @@ export default {
selectPlaceholder: 'เลือก',
},
error: {
- loadFailed: 'โหลดรายละเอียดงานออเดอร์ V4 ไม่สำเร็จ',
+ loadFailed: 'โหลดหน้าจัดการรายการจองไม่สำเร็จ',
actionFailed: 'ดำเนินการไม่สำเร็จ โปรดตรวจข้อมูลตอบกลับจากหลังบ้าน',
},
},
diff --git a/client/src/i18n/locales/zh-CN.ts b/client/src/i18n/locales/zh-CN.ts
index 59550ef..e769366 100644
--- a/client/src/i18n/locales/zh-CN.ts
+++ b/client/src/i18n/locales/zh-CN.ts
@@ -452,12 +452,12 @@ export default {
order: {
summary: '订单摘要',
taskQueue: '同订单任务队列',
- v4TaskTimeline: 'V4 订单任务时间线',
- v4TaskTimelineEmpty: '暂无 V4 订单任务。',
- v4TaskTimelineHint: '只展示订单任务和任务卡安全摘要;处理动作请进入 V4 任务详情页完成。',
- v4OverviewTitle: 'V4 当前确认快照',
- v4OverviewSubtitle: '仅来自已确认任务卡,不使用未确认 AI 建议。',
- v4OverviewEmpty: '暂无已确认的 V4 订单快照。',
+ v4TaskTimeline: '处理记录',
+ v4TaskTimelineEmpty: '暂无处理记录。',
+ v4TaskTimelineHint: '只展示来源邮件处理记录和事项状态摘要;办理动作请进入订单事项办理页完成。',
+ v4OverviewTitle: '订单信息总览',
+ v4OverviewSubtitle: '仅来自已确认事项,不使用未确认建议。',
+ v4OverviewEmpty: '暂无已确认的订单信息。',
accountCode: 'Account 代码',
accountName: 'Account 名称',
marketCode: 'Market 代码',
@@ -466,31 +466,36 @@ export default {
rateCode: 'Rate Code',
roomItems: '已确认房型房量',
latestConfirmedAt: '最近确认时间',
- traceCardStatus: 'Trace 卡片状态',
- roomingListCardStatus: 'Rooming List 卡片状态',
- paymentCardStatus: 'Payment 卡片状态',
+ traceCardStatus: '跟进事项状态',
+ roomingListCardStatus: '房表事项状态',
+ paymentCardStatus: '付款凭证状态',
nextV4ActionTitle: '下一步处理',
- nextV4ActionSubtitle: '订单总览只提供入口,不直接确认、复核或编辑任务卡。',
- nextV4ActionType: '动作类型',
- nextV4ActionStatus: '动作状态',
- nextV4CardId: '任务卡编号',
- openOrderTaskCount: '未完成 V4 任务数',
- enterV4Task: '进入处理页',
- nextActionConfirm: '确认任务卡',
- nextActionReview: '人工复核',
- nextActionNone: '无待处理动作',
- nextV4ActionEmpty: '暂无下一步 V4 处理动作。',
- relatedSourceMessages: '关联来源消息',
+ nextV4ActionSubtitle: '订单总览只提供入口,不直接确认、复核或编辑事项。',
+ nextV4ActionType: '下一步',
+ nextV4ActionStatus: '处理状态',
+ nextV4CardId: '事项编号',
+ openOrderTaskCount: '待处理事项数',
+ enterV4Task: '进入办理页',
+ nextActionConfirm: '确认事项',
+ nextActionReview: '需要复核',
+ nextActionNone: '无待处理事项',
+ nextStep: '处理事项',
+ nextStepConfirm: '有事项待确认',
+ nextStepReview: '有事项需要复核',
+ nextV4ActionEmpty: '暂无下一步处理事项。',
+ relatedSourceMessages: '关联来源邮件',
relatedSourceMessagesHint: '只展示邮件头和会话摘要;全文请进入邮件会话页查看。',
- relatedSourceMessagesEmpty: '暂无关联来源消息。',
+ relatedSourceMessagesEmpty: '暂无关联来源邮件。',
externalConversationId: '外部会话编号',
latestActivityAt: '最近活动时间',
- v4CardsEmpty: '暂无任务卡摘要。',
- cardId: '任务卡编号',
+ v4CardsEmpty: '暂无事项状态摘要。',
+ cardStatusSummary: '事项状态摘要',
+ cardId: '事项编号',
reviewStatus: '复核状态',
confirmedBy: '确认人',
confirmedAt: '确认时间',
- sourceEventIndex: '来源事件 {index}',
+ sourceEventIndex: '来源邮件事件 {index}',
+ processingRecord: '来源邮件处理记录',
sourceMessage: '来源消息',
businessKeySource: '业务号来源',
updatedAt: '最近更新',
@@ -508,25 +513,35 @@ export default {
sourceSummaryPending: '订单详情任务时间线的来源邮件摘要字段待后端补充;请从具体任务进入后查看。',
},
taskList: {
- title: '任务列表',
- subtitle: '按任务查看预订待处理队列',
+ title: '待处理预订事项',
+ subtitle: '查看需要确认、复核或已读的预订事项',
keyword: '订单 / 邮件关键词',
keywordPlaceholder: '订单号 / 邮件关键词',
taskType: '任务类型',
taskSubtype: '任务细分',
- tableTitle: '任务数据',
- totalSuffix: '条任务',
+ tableTitle: '预订事项',
+ totalSuffix: '条事项',
noSourceSubject: '暂无来源主题',
sourceReceivedAt: '来源时间',
+ businessStatus: '事项状态',
+ filterPending: '待处理',
+ filterReview: '需要复核',
+ filterCompleted: '已完成',
+ filterSourceNotification: '来源通知',
+ noOpenItems: '暂无需要处理的预订事项',
+ noPendingItems: '暂无待确认的预订事项',
+ noReviewItems: '暂无需要复核的预订事项',
+ noCompletedItems: '暂无已完成的预订事项',
+ noSourceNotifications: '暂无来源通知',
itemType: '工作项类型',
- itemTypeOrderTask: '订单任务',
+ itemTypeOrderTask: '预订事项',
itemTypeSourceNotification: '来源通知',
orderTaskStatus: '订单任务状态',
cardStatus: '卡片状态',
orderUnresolved: '订单归属待确认',
sourceNotificationNoOrder: '不关联订单',
- sourceNotificationNoOrderHint: 'S10/S99 只读来源通知',
- cardCountsTotal: '共 {total} 张卡',
+ sourceNotificationNoOrderHint: '该邮件不关联订单,请查看邮件内容。',
+ cardCountsTotal: '共 {total} 个事项',
cardCountsPending: '待确认 {count}',
cardCountsReview: '待复核 {count}',
cardCountsConfirmed: '已确认 {count}',
@@ -537,42 +552,50 @@ export default {
queueAll: '全部队列状态',
queueIncluded: '参与队列',
queueExcluded: '不参与队列',
- viewTask: '查看任务',
+ viewTask: '处理事项',
viewOrder: '查看订单',
viewConversation: '查看邮件会话',
sourceOnlyOrderLabel: '来源消息只读',
hiddenTechnicalOrder: '不关联可见订单',
columns: {
- item: '工作项',
+ item: '事项类型',
task: '任务',
order: '订单',
- card: '任务卡',
+ card: '事项',
status: '状态',
source: '来源邮件',
- cards: '卡片',
+ cards: '事项进度',
queue: '队列',
},
},
taskV4: {
- title: 'V4 订单任务详情',
- subtitle: '按后端 V4 任务卡模型处理当前订单任务',
- orderTaskSummary: '订单任务摘要',
- orderTaskId: '订单任务编号',
+ title: '订单事项办理',
+ subtitle: '按来源邮件办理当前订单事项',
+ orderTaskSummary: '事项摘要',
+ orderTaskId: '事项编号',
orderReference: '订单引用',
- targetBookingType: '目标预订类型',
- targetLocator: '目标定位信息',
- targetResolutionStatus: '归属确认状态',
- cardCounts: '任务卡统计',
- sourceMessageCard: '来源邮件卡',
- basicInformationCard: '基础信息卡',
- businessCards: '业务任务卡',
+ targetBookingType: '预订类型',
+ targetLocator: '订单线索',
+ targetResolutionStatus: '订单归属',
+ cardCounts: '事项统计',
+ sourceMessageCard: '来源邮件',
+ basicInformationCard: '预订基础信息',
+ roomInformationCard: '房型与日期',
+ paymentCard: '付款凭证',
+ businessCards: '预订事项',
adapterContractErrors: '适配契约诊断',
noAdapterContractErrors: '暂无适配契约异常。',
- noBusinessCards: '暂无业务任务卡。',
+ noBusinessCards: '暂无需要办理的预订事项。',
displayPayload: '展示摘要',
rawCardPayload: '卡片安全摘要',
readonlyReason: '不可操作原因',
blockedReason: '阻塞原因',
+ bookingTypeGroup: '团队预订',
+ bookingTypeFit: '散客预订',
+ bookingTypeUnknown: '预订类型待确认',
+ targetResolved: '已确认',
+ targetUnresolved: '待确认',
+ targetPending: '待确认',
confirmCard: '确认卡片',
confirmingCard: '确认中',
resolveReview: '提交复核',
@@ -624,14 +647,14 @@ export default {
fileMeta: '{type} · {size}',
},
roomingList: {
- title: 'Rooming List / 房表事项',
+ title: '房表事项',
targetOrder: '目标订单',
unboundOrder: '暂未绑定可见订单',
description: '当前来源消息包含需要人工处理的 Rooming List 事项,请在酒店内部流程处理后确认该卡片。',
confirmHint: '第一版仅确认该事项已由人工处理,不展示或处理名单明细和附件内容。',
},
trace: {
- title: 'Trace / 追踪备注事项',
+ title: '跟进事项',
itemLabel: '事项 {index}',
generalTitle: '普通事项',
extraBedTitle: '加床事项',
@@ -643,7 +666,6 @@ export default {
stale: '当前目录为过期快照,仍可选择,最终以后端校验为准。',
warning: '目录提示:{warning}',
error: '目录暂不可用,请刷新后重试。',
- catalogMeta: '目录来源:{source},版本:{version}',
accountDerived: '选择后将参考目录展示 Market:{market} / Source:{source},最终以后端确认结果为准。',
currentNotInCatalog: '原值“{value}”不在当前目录中,请重新选择。',
},
@@ -655,6 +677,7 @@ export default {
attachments: '附件',
excerpt: '邮件片段',
body: '当前邮件正文',
+ bodyCollapsed: '邮件正文已折叠,展开后读取当前触发邮件正文。',
bodyLoading: '正在加载当前邮件正文...',
bodyLoadFailed: '当前邮件正文暂时无法加载,可进入邮件会话查看。',
noBody: '暂无当前邮件正文。',
@@ -671,7 +694,7 @@ export default {
selectPlaceholder: '请选择',
},
error: {
- loadFailed: 'V4 订单任务详情加载失败。',
+ loadFailed: '订单事项办理页加载失败。',
actionFailed: '操作失败,请检查后端返回信息。',
},
},
diff --git a/client/src/tests/reservationService.spec.ts b/client/src/tests/reservationService.spec.ts
index ba31da8..6530a5a 100644
--- a/client/src/tests/reservationService.spec.ts
+++ b/client/src/tests/reservationService.spec.ts
@@ -248,7 +248,7 @@ describe('reservationService real API mode', () => {
page_size: 20,
})
await fetchReservationV4RateCodeLookups({
- keyword: 'BAR',
+ keyword: 'GRPA2-850UP',
page_num: 1,
page_size: 20,
})
@@ -265,7 +265,7 @@ describe('reservationService real API mode', () => {
)
expect(fetchMock).toHaveBeenNthCalledWith(
3,
- '/api/reservation/lookups/rate-codes?hotel_id=HOTEL-BKK&keyword=BAR&page_num=1&page_size=20',
+ '/api/reservation/lookups/rate-codes?hotel_id=HOTEL-BKK&keyword=GRPA2-850UP&page_num=1&page_size=20',
expect.objectContaining({ method: 'GET' }),
)
})
@@ -336,7 +336,7 @@ describe('reservationService real API mode', () => {
field_overrides: [
{
field_pointer: '/business_fields/room_items/0/room_type_code',
- value: 'TWN',
+ value: 'RM2',
},
],
})
@@ -357,7 +357,7 @@ describe('reservationService real API mode', () => {
field_overrides: [
{
field_pointer: '/business_fields/room_items/0/room_type_code',
- value: 'TWN',
+ value: 'RM2',
},
],
}),
diff --git a/client/src/tests/reservationV4FieldRules.spec.ts b/client/src/tests/reservationV4FieldRules.spec.ts
index a7059b2..f24005e 100644
--- a/client/src/tests/reservationV4FieldRules.spec.ts
+++ b/client/src/tests/reservationV4FieldRules.spec.ts
@@ -37,12 +37,12 @@ describe('reservationV4FieldRules', () => {
it('builds initial values from backend fields using field pointers', () => {
const fields = [
createV4Field('/basic_information/account_code', { value: 'HANATOUR' }),
- createV4Field('/business_fields/after/room_items/0/room_type_code', { value: 'TWN' }),
+ createV4Field('/business_fields/after/room_items/0/room_type_code', { value: 'RM2' }),
]
expect(buildReservationV4InitialFieldValues(fields)).toEqual({
'/basic_information/account_code': 'HANATOUR',
- '/business_fields/after/room_items/0/room_type_code': 'TWN',
+ '/business_fields/after/room_items/0/room_type_code': 'RM2',
})
})
@@ -55,7 +55,7 @@ describe('reservationV4FieldRules', () => {
raw_readonly: true,
}),
createV4Field('/business_fields/after/room_items/0/room_type_code', {
- value: 'KING',
+ value: 'SU1',
edit_scope: 'MANUAL_REVIEW_ONLY',
write_target: 'REVIEW_RESOLUTION_FIELD_OVERRIDES',
}),
@@ -64,7 +64,7 @@ describe('reservationV4FieldRules', () => {
const payload = buildReservationV4ConfirmedPayload(fields, {
'/basic_information/account_code': 'HANATOUR',
'/basic_information/read_only_label': 'SHOULD_NOT_SEND',
- '/business_fields/after/room_items/0/room_type_code': 'TWN',
+ '/business_fields/after/room_items/0/room_type_code': 'RM2',
})
expect(payload).toEqual({
@@ -82,19 +82,19 @@ describe('reservationV4FieldRules', () => {
write_target: 'REVIEW_RESOLUTION_FIELD_OVERRIDES',
}),
createV4Field('/room_information/final_values/room_items/0/room_type_raw', {
- value: 'TWN',
+ value: 'RM2',
editable: false,
write_target: 'NONE',
}),
]
expect(buildReservationV4ReviewOverrides(fields, {
- '/room_information/final_values/room_items/0/room_type_code': 'TWN',
+ '/room_information/final_values/room_items/0/room_type_code': 'RM2',
'/room_information/final_values/room_items/0/room_type_raw': 'Twin',
})).toEqual([
{
field_pointer: '/room_information/final_values/room_items/0/room_type_code',
- value: 'TWN',
+ value: 'RM2',
},
])
})
@@ -111,10 +111,10 @@ describe('reservationV4FieldRules', () => {
expect(isReservationV4ConfirmWritableField(confirmField)).toBe(false)
expect(isReservationV4ReviewWritableField(reviewField)).toBe(false)
expect(buildReservationV4ConfirmedPayload([confirmField], {
- '/business_fields/after/room_items/0/rate_code': 'BAR',
+ '/business_fields/after/room_items/0/rate_code': 'GRPA2-850UP',
})).toEqual({})
expect(buildReservationV4ReviewOverrides([reviewField], {
- '/business_fields/after/room_items/0/room_type_code': 'TWN',
+ '/business_fields/after/room_items/0/room_type_code': 'RM2',
})).toEqual([])
})
@@ -184,7 +184,7 @@ describe('reservationV4FieldRules', () => {
expect(buildReservationV4ReviewOverrides(fields, {
'/source_message/subject': 'SHOULD_NOT_SEND',
'/room_information/final_values/arrival_date': '2026-07-26',
- '/business_fields/after/room_items/0/room_type_code': 'TWN',
+ '/business_fields/after/room_items/0/room_type_code': 'RM2',
})).toEqual([
{
field_pointer: '/room_information/final_values/arrival_date',
@@ -192,7 +192,7 @@ describe('reservationV4FieldRules', () => {
},
{
field_pointer: '/business_fields/after/room_items/0/room_type_code',
- value: 'TWN',
+ value: 'RM2',
},
])
})
@@ -203,7 +203,7 @@ describe('reservationV4FieldRules', () => {
group_code: 'GRP-001',
raw_evidence: 'raw email body',
nested: {
- room_type_code: 'TWN',
+ room_type_code: 'RM2',
oss_url: 'https://oss.example/private-file.pdf',
},
safe_url_value: 'https://oss.example/also-hidden.pdf',
@@ -211,7 +211,7 @@ describe('reservationV4FieldRules', () => {
}, 'empty', 'hidden')
expect(text).toContain('GRP-001')
- expect(text).toContain('TWN')
+ expect(text).toContain('RM2')
expect(text).not.toContain('raw email body')
expect(text).not.toContain('https://oss.example')
expect(text).not.toContain('oss_url')
diff --git a/client/src/tests/reservationV4Views.spec.ts b/client/src/tests/reservationV4Views.spec.ts
index d136cdb..288836a 100644
--- a/client/src/tests/reservationV4Views.spec.ts
+++ b/client/src/tests/reservationV4Views.spec.ts
@@ -52,7 +52,7 @@ describe('reservation V4 pages', () => {
mockCatalogLookups()
})
- it('renders V4 order task cards, loads lookup options and confirms only selected codes', async () => {
+ it('renders reservation handling cards, hides default diagnostics and confirms only selected codes', async () => {
const detail = createOrderTaskDetail()
vi.mocked(service.fetchReservationV4OrderTaskDetail).mockResolvedValue(detail)
vi.mocked(service.confirmReservationV4OrderTaskCard).mockResolvedValue({
@@ -69,11 +69,18 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
- expect(wrapper.text()).toContain('V4 订单任务详情')
+ expect(wrapper.text()).toContain('订单事项办理')
expect(wrapper.text()).toContain('Booking Request')
- expect(wrapper.text()).toContain('基础信息卡')
- expect(wrapper.text()).toContain('业务任务卡')
- expect(wrapper.text()).toContain('Backend contract issue')
+ expect(wrapper.text()).toContain('预订基础信息')
+ expect(wrapper.text()).toContain('预订事项')
+ expect(wrapper.text()).toContain('房型与日期')
+ expect(wrapper.text()).not.toContain('V4 订单任务详情')
+ expect(wrapper.text()).not.toContain('Backend contract issue')
+ expect(wrapper.text()).not.toContain('card-basic')
+ expect(wrapper.text()).not.toContain('card-room')
+ expect(wrapper.text()).not.toContain('#9001')
+ expect(wrapper.text()).not.toContain(zhCN.taskV4.orderTaskId)
+ expect(wrapper.text()).not.toContain(zhCN.taskV4.displayPayload)
expect(service.fetchReservationV4AccountLookups).toHaveBeenCalledWith({
hotel_id: 'HOTEL-TEST',
page_num: 1,
@@ -102,7 +109,7 @@ describe('reservation V4 pages', () => {
})
})
- it('keeps V4 task card layout classes for styled card metadata and payload summary', async () => {
+ it('keeps task card layout classes and places the primary card action in the card header', async () => {
const detail = createOrderTaskDetail()
detail.business_cards[0]!.card_type = 'VOUCHER'
detail.business_cards[0]!.display_payload = {
@@ -119,10 +126,10 @@ describe('reservation V4 pages', () => {
expect(wrapper.findAll('.task-card-section')).toHaveLength(2)
expect(wrapper.find('.task-card-section__header').exists()).toBe(true)
- expect(wrapper.find('.card-meta-grid').exists()).toBe(true)
- expect(wrapper.find('.card-meta-item').text()).toContain(zhCN.task.reviewStatus)
- expect(wrapper.find('details.safe-payload > summary').text()).toContain(zhCN.taskV4.displayPayload)
- expect(wrapper.find('.card-actions .primary-button').text()).toContain(zhCN.taskV4.confirmCard)
+ expect(wrapper.find('.card-meta-grid').exists()).toBe(false)
+ expect(wrapper.find('details.safe-payload').exists()).toBe(false)
+ expect(wrapper.find('.task-card-section__header .primary-button').text()).toContain(zhCN.taskV4.confirmCard)
+ expect(wrapper.find('.card-actions .primary-button').exists()).toBe(false)
})
it('does not render direct attachment URLs from V4 source message payload', async () => {
@@ -184,17 +191,16 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
- expect(service.fetchSourceMessageConversation).toHaveBeenCalledWith('30001')
const pageText = wrapper.text()
const basicIndex = pageText.indexOf(zhCN.taskV4.basicInformationCard)
const businessIndex = pageText.indexOf(zhCN.taskV4.businessCards)
- const sourceIndex = pageText.indexOf(zhCN.taskV4.sourceMessageCard)
expect(basicIndex).toBeGreaterThanOrEqual(0)
expect(businessIndex).toBeGreaterThan(basicIndex)
- expect(sourceIndex).toBeGreaterThan(businessIndex)
const sections = wrapper.findAll('.th-section')
expect(sections[sections.length - 1]?.text()).toContain(zhCN.taskV4.sourceMessageCard)
- expect(pageText).toContain('Current trigger email body opening.')
+ expect(sections[sections.length - 1]?.text()).toContain(zhCN.taskV4.sourceMessage.bodyCollapsed)
+ expect(service.fetchSourceMessageConversation).not.toHaveBeenCalled()
+ expect(pageText).not.toContain('Current trigger email body opening.')
expect(pageText).not.toContain('OLDER_MESSAGE_BODY')
expect(pageText).not.toContain('REPLY_MESSAGE_BODY')
expect(pageText).not.toContain('CURRENT_BODY_TAIL')
@@ -209,6 +215,8 @@ describe('reservation V4 pages', () => {
await expandButton!.trigger('click')
await flushPromises()
+ expect(service.fetchSourceMessageConversation).toHaveBeenCalledWith('30001')
+ expect(wrapper.text()).toContain('Current trigger email body opening.')
expect(wrapper.text()).toContain('CURRENT_BODY_TAIL')
expect(wrapper.text()).toContain(zhCN.taskV4.sourceMessage.collapseBody)
})
@@ -228,6 +236,17 @@ describe('reservation V4 pages', () => {
await flushPromises()
expect(wrapper.text()).toContain('Please book one twin room.')
+ expect(wrapper.text()).not.toContain(zhCN.taskV4.sourceMessage.bodyLoadFailed)
+ expect(service.fetchSourceMessageConversation).not.toHaveBeenCalled()
+
+ const expandButton = wrapper
+ .findAll('button')
+ .find((button) => button.text().includes(zhCN.taskV4.sourceMessage.showFullBody))
+ expect(expandButton).toBeTruthy()
+ await expandButton!.trigger('click')
+ await flushPromises()
+
+ expect(service.fetchSourceMessageConversation).toHaveBeenCalledWith('30001')
expect(wrapper.text()).toContain(zhCN.taskV4.sourceMessage.bodyLoadFailed)
expect(wrapper.text()).toContain(zhCN.task.viewConversation)
})
@@ -250,7 +269,7 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
- expect(wrapper.text()).toContain('Sanitized current email opening.')
+ expect(wrapper.text()).not.toContain('Sanitized current email opening.')
expect(wrapper.text()).not.toContain('SANITIZED_HTML_TAIL')
expect(wrapper.text()).not.toContain('RAW_HTML_SHOULD_NOT_RENDER')
@@ -261,6 +280,7 @@ describe('reservation V4 pages', () => {
await expandButton!.trigger('click')
await flushPromises()
+ expect(wrapper.text()).toContain('Sanitized current email opening.')
expect(wrapper.text()).toContain('SANITIZED_HTML_TAIL')
expect(wrapper.html()).toContain('Sanitized current email opening.')
expect(wrapper.html()).not.toContain('RAW_HTML_SHOULD_NOT_RENDER')
@@ -283,6 +303,14 @@ describe('reservation V4 pages', () => {
)
await flushPromises()
+ expect(wrapper.text()).not.toContain('Plain text fallback body.')
+ const expandButton = wrapper
+ .findAll('button')
+ .find((button) => button.text().includes(zhCN.taskV4.sourceMessage.showFullBody))
+ expect(expandButton).toBeTruthy()
+ await expandButton!.trigger('click')
+ await flushPromises()
+
expect(wrapper.text()).toContain('Plain text fallback body.')
expect(wrapper.text()).not.toContain('SANITIZED_HTML_SHOULD_NOT_RENDER')
expect(wrapper.text()).not.toContain('RAW_HTML_SHOULD_NOT_RENDER')
@@ -373,7 +401,7 @@ describe('reservation V4 pages', () => {
const paymentCard = wrapper
.findAll('.task-card-section')
- .find((section) => section.text().includes(zhCN.cardType.PAYMENT))
+ .find((section) => section.text().includes(zhCN.taskV4.paymentCard))
expect(paymentCard).toBeTruthy()
await paymentCard!.find('button.primary-button').trigger('click')
await flushPromises()
@@ -409,9 +437,8 @@ describe('reservation V4 pages', () => {
expect(roomingListCard.text()).toContain('GRP-001')
expect(roomingListSection!.findAll('a').filter((link) => link.text() === zhCN.task.viewOrder)).toHaveLength(1)
expect(wrapper.text()).toContain(zhCN.taskV4.sourceMessageCard)
- expect(wrapper.text().indexOf(zhCN.taskV4.sourceMessageCard)).toBeGreaterThan(
- wrapper.text().indexOf(zhCN.taskV4.roomingList.title),
- )
+ const sections = wrapper.findAll('.th-section')
+ expect(sections[sections.length - 1]?.text()).toContain(zhCN.taskV4.sourceMessageCard)
expect(roomingListSection!.find('details.safe-payload').exists()).toBe(false)
expect(roomingListSection!.text()).not.toContain('ROW_SHOULD_NOT_RENDER')
@@ -592,13 +619,13 @@ describe('reservation V4 pages', () => {
.find((section) => section.find('[data-testid="trace-card"]').exists())
expect(traceSection).toBeTruthy()
expect(traceSection!.text()).toContain('SET EXTRA BED')
- expect(traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').text()).toContain('TWN - Twin')
+ expect(traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').text()).toContain('RM2')
const roomCountInput = traceSection!.find('input[name="/trace_items/0/extra_bed_room_count"]')
expect(roomCountInput.attributes('type')).toBe('number')
expect(roomCountInput.attributes('min')).toBe('1')
expect(roomCountInput.attributes('step')).toBe('1')
- await traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').setValue('TWN')
+ await traceSection!.find('select[name="/trace_items/0/target_room_type_code"]').setValue('RM2')
await roomCountInput.setValue('0')
await traceSection!.find('select[name="/trace_items/0/department_code"]').setValue('FO+HSK')
await traceSection!.find('button.primary-button').trigger('click')
@@ -616,7 +643,7 @@ describe('reservation V4 pages', () => {
confirmed_payload: {
trace_items: [
{
- target_room_type_code: 'TWN',
+ target_room_type_code: 'RM2',
extra_bed_room_count: 2,
department_code: 'FO+HSK',
},
@@ -770,11 +797,11 @@ describe('reservation V4 pages', () => {
group_block_name: '前端修正团队名',
arrival_date: '2026-07-26',
departure_date: '2026-07-29',
- rate_code: 'GROUP',
+ rate_code: 'GRPA2-850UP',
group_booking_status: 'DEF',
room_items: [
{
- room_type_code: 'TWN',
+ room_type_code: 'RM2',
room_count: 2,
},
],
@@ -858,13 +885,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-07-26',
departure_date: '2026-07-29',
nights: 3,
- rate_code: 'GROUP',
+ rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'TEN',
group_booking_status_label: 'TEN-Tentative',
room_items: [
{
- room_type_code: 'TWN',
+ room_type_code: 'RM2',
room_count: 2,
},
],
@@ -874,7 +901,7 @@ describe('reservation V4 pages', () => {
departure_date: '2026-07-31',
room_items: [
{
- room_type_code: 'DBL',
+ room_type_code: 'RM3',
room_count: 3,
},
],
@@ -884,13 +911,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-07-27',
departure_date: '2026-07-31',
nights: 4,
- rate_code: 'GROUP',
+ rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'TEN',
group_booking_status_label: 'TEN-Tentative',
room_items: [
{
- room_type_code: 'DBL',
+ room_type_code: 'RM3',
room_count: 3,
},
],
@@ -953,13 +980,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-08-01',
departure_date: '2026-08-05',
nights: 4,
- rate_code: 'GROUP',
+ rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'DEF',
group_booking_status_label: 'DEF-Definite',
room_items: [
{
- room_type_code: 'KING',
+ room_type_code: 'SU1',
room_count: 1,
},
],
@@ -969,13 +996,13 @@ describe('reservation V4 pages', () => {
arrival_date: '2026-08-01',
departure_date: '2026-08-05',
nights: 4,
- rate_code: 'GROUP',
+ rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'DEF',
group_booking_status_label: 'DEF-Definite',
room_items: [
{
- room_type_code: 'KING',
+ room_type_code: 'SU1',
room_count: 1,
},
],
@@ -994,7 +1021,7 @@ describe('reservation V4 pages', () => {
expect(roomCard.text()).toContain(zhCN.taskV4.roomInformation.currentValues)
expect(roomCard.text()).toContain(zhCN.taskV4.roomInformation.finalValues)
expect(roomCard.text()).toContain('GRP-V4-RI-CANCEL-001')
- expect(roomCard.text()).toContain('KING')
+ expect(roomCard.text()).toContain('SU1')
expect(roomCard.find('input[name="/room_information/final_values/arrival_date"]').exists()).toBe(false)
expect(roomCard.find('select[name="/room_information/final_values/group_booking_status"]').exists()).toBe(false)
})
@@ -1027,7 +1054,7 @@ describe('reservation V4 pages', () => {
await flushPromises()
const roomCard = wrapper.find('[data-testid="room-information-card"]')
- await roomCard.find('select[name="/room_information/final_values/room_items/0/room_type_code"]').setValue('TWN')
+ await roomCard.find('select[name="/room_information/final_values/room_items/0/room_type_code"]').setValue('RM2')
await wrapper.find('input[name="v4_confirmed_order_id"]').setValue('order-2001')
await wrapper.find('textarea').setValue('confirmed by email evidence')
await roomCard.find('button').trigger('click')
@@ -1040,7 +1067,7 @@ describe('reservation V4 pages', () => {
field_overrides: [
{
field_pointer: '/room_information/final_values/room_items/0/room_type_code',
- value: 'TWN',
+ value: 'RM2',
},
],
})
@@ -1073,8 +1100,8 @@ describe('reservation V4 pages', () => {
expect(wrapper.text()).toContain(zhCN.taskV4.lookup.empty)
expect(wrapper.text()).toContain(zhCN.taskV4.lookup.stale)
expect(wrapper.text()).toContain('PMS sync is stale')
- expect(wrapper.text()).toContain('PMS_SYNC')
- expect(wrapper.text()).toContain('stale-v1')
+ expect(wrapper.text()).not.toContain('PMS_SYNC')
+ expect(wrapper.text()).not.toContain('stale-v1')
})
it('does not submit a catalog field value that is missing from the loaded lookup result', async () => {
@@ -1359,8 +1386,8 @@ function mockCatalogLookups() {
stale: false,
items: [
{
- code: 'TWN',
- display_name: 'Twin',
+ code: 'RM2',
+ display_name: 'RM2',
status: 'ACTIVE',
catalog_source: 'SYSTEM_MANAGED',
market_code: null,
@@ -1386,8 +1413,8 @@ function mockCatalogLookups() {
stale: false,
items: [
{
- code: 'GROUP',
- display_name: 'Group Rate',
+ code: 'GRPA2-850UP',
+ display_name: 'GRPA2-850UP',
status: 'ACTIVE',
catalog_source: 'SYSTEM_MANAGED',
market_code: null,
@@ -1750,7 +1777,7 @@ function useTraceBusinessCard(
itemType === 'EXTRA_BED'
? {
item_type: 'EXTRA_BED',
- target_room_type_code: 'TWN',
+ target_room_type_code: 'RM2',
extra_bed_room_count: 1,
department_code: 'FO',
content: 'CONTENT_SHOULD_NOT_RENDER',
@@ -1770,7 +1797,7 @@ function useTraceBusinessCard(
? [
createField('/trace_items/0/target_room_type_code', {
display_name: 'Target Room Type',
- value: 'TWN',
+ value: 'RM2',
options_source: 'RESERVATION_V4_ROOM_TYPE_CATALOG',
control_type: 'SELECT',
edit_scope: isReviewRequired ? 'REVIEW' : 'CONFIRM',
@@ -1891,13 +1918,13 @@ function createRoomInformationDisplayPayload(overrides: {
arrival_date: '2026-07-26',
departure_date: '2026-07-29',
nights: 3,
- rate_code: 'GROUP',
+ rate_code: 'GRPA2-850UP',
breakfast_included: true,
group_booking_status: 'TEN',
group_booking_status_label: 'TEN-Tentative',
room_items: [
{
- room_type_code: 'TWN',
+ room_type_code: 'RM2',
room_count: 2,
},
],
@@ -1962,7 +1989,7 @@ function createRoomInformationFields(reviewMode = false): ReservationV4TaskCardR
}),
createField('/room_information/final_values/rate_code', {
display_name: 'Rate Code',
- value: 'GROUP',
+ value: 'GRPA2-850UP',
required: true,
control_type: 'SELECT',
options_source: 'RESERVATION_V4_RATE_CODE_CATALOG',
@@ -1972,7 +1999,7 @@ function createRoomInformationFields(reviewMode = false): ReservationV4TaskCardR
}),
createField('/room_information/final_values/room_items/0/room_type_code', {
display_name: '房型代码',
- value: reviewMode ? '' : 'TWN',
+ value: reviewMode ? '' : 'RM2',
required: true,
control_type: 'SELECT',
options_source: 'RESERVATION_V4_ROOM_TYPE_CATALOG',
diff --git a/client/src/tests/reservationViews.spec.ts b/client/src/tests/reservationViews.spec.ts
index 651ee8a..097ae2d 100644
--- a/client/src/tests/reservationViews.spec.ts
+++ b/client/src/tests/reservationViews.spec.ts
@@ -570,32 +570,36 @@ describe('reservation P0 views', () => {
vi.mocked(service.fetchSourceMessageConversation).mockReset()
})
- it('renders the V4 order overview, next action, related source messages and safe card timeline', async () => {
+ it('renders the user-facing order overview, next action, related source emails and processing records', async () => {
vi.mocked(service.fetchReservationOrderDetail).mockResolvedValue(createOrderDetailResult())
const wrapper = await mountWithPlugins(ReservationOrderDetailView, '/reservation/orders/20001')
await vi.dynamicImportSettled()
- expect(wrapper.text()).toContain('V4 当前确认快照')
+ expect(wrapper.text()).toContain('订单信息总览')
expect(wrapper.text()).toContain('QBD_TRAVEL')
expect(wrapper.text()).toContain('Q.B.D. TRAVEL GROUP CO., LTD')
expect(wrapper.text()).toContain('2026-07-26')
expect(wrapper.text()).toContain('2026-07-29')
expect(wrapper.text()).toContain('RM1 × 2')
expect(wrapper.text()).toContain('下一步处理')
- expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').text()).toContain('进入处理页')
+ expect(wrapper.find('a[href="/reservation/order-tasks/9001"]').text()).toContain('进入办理页')
expect(wrapper.find('.primary-link[href="/reservation/order-tasks/9001"]').exists()).toBe(true)
- expect(wrapper.text()).toContain('关联来源消息')
+ expect(wrapper.text()).toContain('关联来源邮件')
expect(wrapper.text()).toContain('v4@example.test')
expect(wrapper.text()).toContain('查看邮件会话')
expect(wrapper.find('a[href="/reservation/source-messages/30003/conversation"]').exists()).toBe(true)
- expect(wrapper.text()).toContain(zhCN.order.v4TaskTimeline)
- expect(wrapper.text()).toContain('order-ref-1')
+ expect(wrapper.text()).toContain('处理记录')
+ expect(wrapper.text()).not.toContain('order-ref-1')
expect(wrapper.text()).toContain('V4 Booking Package')
expect(wrapper.find('.v4-timeline-item__main[href="/reservation/order-tasks/9001"]').exists()).toBe(true)
- expect(wrapper.text()).toContain('基础信息卡')
+ expect(wrapper.text()).toContain('事项状态摘要')
+ expect(wrapper.text()).toContain('预订基础信息')
expect(wrapper.text()).toContain('frontend-query-admin')
expect(wrapper.text()).toContain('待复核')
+ expect(wrapper.text()).not.toContain('V4 当前确认快照')
+ expect(wrapper.text()).not.toContain('任务卡编号')
+ expect(wrapper.text()).not.toContain('card-basic')
expect(wrapper.text()).not.toContain('unsafe ai payload must stay hidden')
expect(wrapper.text()).not.toContain('unsafe display payload must stay hidden')
expect(wrapper.text()).not.toContain('unsafe confirmed payload must stay hidden')
@@ -622,9 +626,9 @@ describe('reservation P0 views', () => {
const wrapper = await mountWithPlugins(ReservationOrderDetailView, '/reservation/orders/20001')
await vi.dynamicImportSettled()
- expect(wrapper.text()).toContain('暂无已确认的 V4 订单快照')
- expect(wrapper.text()).toContain('暂无下一步 V4 处理动作')
- expect(wrapper.text()).toContain('暂无关联来源消息')
+ expect(wrapper.text()).toContain('暂无已确认的订单信息')
+ expect(wrapper.text()).toContain('暂无下一步处理事项')
+ expect(wrapper.text()).toContain('暂无关联来源邮件')
expect(wrapper.text()).toContain(zhCN.order.v4TaskTimelineEmpty)
expect(wrapper.find('a[href^="/reservation/order-tasks/"]').exists()).toBe(false)
})
@@ -830,10 +834,11 @@ describe('reservation P0 views', () => {
expect(wrapper.text()).toContain('GRP-001')
expect(wrapper.text()).toContain('Booking Request')
- expect(wrapper.text()).toContain('订单任务')
+ expect(wrapper.text()).toContain('预订事项')
expect(wrapper.text()).toContain('待确认 1')
expect(wrapper.text()).toContain('待复核 1')
expect(wrapper.text()).toContain('查看邮件会话')
+ expect(wrapper.text()).not.toContain('#9001')
expect(wrapper.text()).not.toContain('接口待补')
expect(service.fetchReservationV4WorkbenchItems).toHaveBeenCalledWith(
expect.objectContaining({
@@ -866,10 +871,12 @@ describe('reservation P0 views', () => {
expect(wrapper.text()).toContain('待确认已读')
expect(wrapper.text()).toContain('查看通知')
expect(wrapper.text()).toContain('查看邮件会话')
+ expect(wrapper.text()).toContain('该邮件不关联订单,请查看邮件内容。')
+ expect(wrapper.text()).not.toContain('S10/S99')
expect(wrapper.text()).not.toContain('查看订单')
})
- it('filters V4 workbench with supported backend fields only', async () => {
+ it('filters the reservation workbench with user-facing filter choices only', async () => {
vi.mocked(service.fetchReservationV4WorkbenchItems)
.mockResolvedValueOnce(createV4WorkbenchResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
.mockResolvedValueOnce(createV4SourceNotificationWorkbenchResult('S10'))
@@ -877,14 +884,18 @@ describe('reservation P0 views', () => {
const wrapper = await mountWithPlugins(ReservationTaskListView)
await vi.dynamicImportSettled()
- const itemTypeOptions = wrapper.find('select[name="item_type"]').findAll('option')
+ const itemTypeOptions = wrapper.find('select[name="business_status"]').findAll('option')
.map((option) => option.attributes('value'))
- expect(itemTypeOptions).toEqual(['', 'ORDER_TASK', 'SOURCE_NOTIFICATION'])
- expect(wrapper.find('select[name="item_type"]').text()).toContain('来源通知')
- expect(wrapper.find('select[name="item_type"]').text()).not.toContain('INFORMATIONAL_MESSAGE')
+ expect(itemTypeOptions).toEqual(['', 'PENDING_CONFIRM', 'REVIEW_REQUIRED', 'COMPLETED', 'SOURCE_NOTIFICATION'])
+ expect(wrapper.find('select[name="business_status"]').text()).toContain('来源通知')
+ expect(wrapper.find('select[name="business_status"]').text()).not.toContain('ORDER_TASK')
+ expect(wrapper.find('select[name="business_status"]').text()).not.toContain('SOURCE_NOTIFICATION')
+ expect(wrapper.find('select[name="item_type"]').exists()).toBe(false)
+ expect(wrapper.find('select[name="order_task_status"]').exists()).toBe(false)
+ expect(wrapper.find('select[name="card_status"]').exists()).toBe(false)
- await wrapper.find('select[name="item_type"]').setValue('SOURCE_NOTIFICATION')
+ await wrapper.find('select[name="business_status"]').setValue('SOURCE_NOTIFICATION')
await vi.dynamicImportSettled()
expect(service.fetchReservationV4WorkbenchItems).toHaveBeenLastCalledWith(
@@ -903,35 +914,45 @@ describe('reservation P0 views', () => {
expect(lastTaskRequest).not.toHaveProperty('route_code')
})
- it('filters and paginates V4 business order tasks with supported backend fields', async () => {
+ it('shows a filter-specific empty state for completed reservation items', async () => {
+ vi.mocked(service.fetchReservationV4WorkbenchItems)
+ .mockResolvedValueOnce(createV4WorkbenchResult('GRP-001', 'Booking Request'))
+ vi.mocked(service.fetchReservationV4OrderTasks)
+ .mockResolvedValueOnce({
+ items: [],
+ page: {
+ page_num: 1,
+ page_size: 20,
+ total: 0,
+ },
+ })
+
+ const wrapper = await mountWithPlugins(ReservationTaskListView)
+ await vi.dynamicImportSettled()
+
+ await wrapper.find('select[name="business_status"]').setValue('COMPLETED')
+ await vi.dynamicImportSettled()
+
+ expect(wrapper.text()).toContain('暂无已完成的预订事项')
+ expect(wrapper.text()).not.toContain('暂无需要处理的预订事项')
+ })
+
+ it('filters and paginates reservation work items with supported backend fields', async () => {
vi.mocked(service.fetchReservationV4WorkbenchItems)
.mockResolvedValueOnce(createV4WorkbenchResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
vi.mocked(service.fetchReservationV4OrderTasks)
- .mockResolvedValueOnce(createV4OrderTaskListResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
- .mockResolvedValueOnce(createV4OrderTaskListResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
.mockResolvedValueOnce(createV4OrderTaskListResult('GRP-001', 'Booking Request', { page_num: 1, page_size: 20, total: 45 }))
.mockResolvedValueOnce(createV4OrderTaskListResult('GRP-002', 'Booking Update', { page_num: 2, page_size: 20, total: 45 }))
const wrapper = await mountWithPlugins(ReservationTaskListView)
await vi.dynamicImportSettled()
- expect(wrapper.find('select[name="order_task_status"]').attributes('disabled')).toBeDefined()
- expect(wrapper.find('select[name="card_status"]').attributes('disabled')).toBeDefined()
-
- await wrapper.find('select[name="item_type"]').setValue('ORDER_TASK')
- await vi.dynamicImportSettled()
- expect(wrapper.find('select[name="order_task_status"]').attributes('disabled')).toBeUndefined()
- expect(wrapper.find('select[name="card_status"]').attributes('disabled')).toBeUndefined()
-
- await wrapper.find('select[name="order_task_status"]').setValue('OPEN')
- await vi.dynamicImportSettled()
- await wrapper.find('select[name="card_status"]').setValue('REVIEW_REQUIRED')
+ await wrapper.find('select[name="business_status"]').setValue('REVIEW_REQUIRED')
await vi.dynamicImportSettled()
const taskListCalls = vi.mocked(service.fetchReservationV4OrderTasks).mock.calls
const filteredTaskRequest = taskListCalls[taskListCalls.length - 1]?.[0] ?? {}
expect(filteredTaskRequest).toMatchObject({
- order_task_status: 'OPEN',
card_status: 'REVIEW_REQUIRED',
page_num: 1,
})
@@ -941,7 +962,6 @@ describe('reservation P0 views', () => {
expect(service.fetchReservationV4OrderTasks).toHaveBeenLastCalledWith(
expect.objectContaining({
- order_task_status: 'OPEN',
card_status: 'REVIEW_REQUIRED',
page_num: 2,
page_size: 20,
diff --git a/client/src/views/reservation/ReservationOrderDetailView.vue b/client/src/views/reservation/ReservationOrderDetailView.vue
index 77d27d6..cae538f 100644
--- a/client/src/views/reservation/ReservationOrderDetailView.vue
+++ b/client/src/views/reservation/ReservationOrderDetailView.vue
@@ -128,27 +128,13 @@
-
- {{ t('order.nextV4ActionType') }}
- - {{ formatNextActionType(nextV4Action.action_type) }}
-
-
-
{{ t('order.nextV4ActionStatus') }}
-
-
- -
-
+ {{ t('order.nextStep') }}
+ {{ nextActionLabel }}
{{ t('order.openOrderTaskCount') }}
{{ nextV4Action.open_order_task_count ?? 0 }}
-
-
{{ t('order.nextV4CardId') }}
- {{ nextV4Action.card_id ?? '-' }}
-
{{ t('order.conversationMessageCount') }}
{{ sourceMessage.conversation_message_count ?? '-' }}