实现V4 Rooming List轻量事项卡

This commit is contained in:
andy
2026-07-21 17:21:01 +07:00
parent 61380a9ed8
commit 0c4f5e298d
7 changed files with 380 additions and 6 deletions

View File

@@ -386,6 +386,134 @@ describe('reservation V4 pages', () => {
expect(JSON.stringify(submittedRequest)).not.toContain('https://oss.example')
})
it('renders Rooming List cards as lightweight action items without rows, attachments or raw payloads', async () => {
const detail = createOrderTaskDetail()
useRoomingListBusinessCard(detail)
useBoundOrder(detail)
vi.mocked(service.fetchReservationV4OrderTaskDetail).mockResolvedValue(detail)
const wrapper = await mountWithPlugins(
ReservationV4OrderTaskDetailView,
'/reservation/order-tasks/9001',
)
await flushPromises()
const roomingListSection = wrapper
.findAll('.task-card-section')
.find((section) => section.find('[data-testid="rooming-list-card"]').exists())
expect(roomingListSection).toBeTruthy()
const roomingListCard = roomingListSection!.find('[data-testid="rooming-list-card"]')
expect(roomingListCard.exists()).toBe(true)
expect(roomingListSection!.text()).toContain(zhCN.taskV4.roomingList.title)
expect(roomingListCard.text()).toContain(zhCN.taskV4.roomingList.description)
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),
)
expect(roomingListSection!.find('details.safe-payload').exists()).toBe(false)
expect(roomingListSection!.text()).not.toContain('ROW_SHOULD_NOT_RENDER')
expect(roomingListSection!.text()).not.toContain('attachment_ids')
expect(roomingListSection!.text()).not.toContain('https://oss.example/private/rooming-list.xlsx')
expect(roomingListSection!.text()).not.toContain('AI_PAYLOAD_SHOULD_NOT_RENDER')
expect(roomingListSection!.text()).not.toContain('PMS_IMPORT_SHOULD_NOT_RENDER')
expect(roomingListSection!.text()).not.toContain('OHIP_SHOULD_NOT_RENDER')
expect(roomingListSection!.text()).not.toContain('Excel')
expect(wrapper.find('[data-testid="payment-attachment-preview"]').exists()).toBe(false)
})
it('shows an explanatory unbound order label for Rooming List cards without target order clues', async () => {
const detail = createOrderTaskDetail()
useRoomingListBusinessCard(detail)
detail.order_task.display_order_key = null
detail.order_task.order_ref = null
detail.order_task.target_locator_type = null
detail.order_task.target_locator_value = null
vi.mocked(service.fetchReservationV4OrderTaskDetail).mockResolvedValue(detail)
const wrapper = await mountWithPlugins(
ReservationV4OrderTaskDetailView,
'/reservation/order-tasks/9001',
)
await flushPromises()
const roomingListCard = wrapper.find('[data-testid="rooming-list-card"]')
expect(roomingListCard.text()).toContain(zhCN.taskV4.roomingList.unboundOrder)
})
it('confirms Rooming List cards with version only and applies the refreshed backend detail', async () => {
const detail = createOrderTaskDetail()
useRoomingListBusinessCard(detail)
const confirmedDetail = createOrderTaskDetail()
useRoomingListBusinessCard(confirmedDetail, 'CONFIRMED')
vi.mocked(service.fetchReservationV4OrderTaskDetail).mockResolvedValue(detail)
vi.mocked(service.confirmReservationV4OrderTaskCard).mockResolvedValue(confirmedDetail)
const wrapper = await mountWithPlugins(
ReservationV4OrderTaskDetailView,
'/reservation/order-tasks/9001',
)
await flushPromises()
const roomingListSection = wrapper
.findAll('.task-card-section')
.find((section) => section.find('[data-testid="rooming-list-card"]').exists())
expect(roomingListSection).toBeTruthy()
await roomingListSection!.find('button.primary-button').trigger('click')
await flushPromises()
expect(service.confirmReservationV4OrderTaskCard).toHaveBeenCalledWith('9001', 'card-rooming-list', {
version: 12,
})
const submittedRequest = vi.mocked(service.confirmReservationV4OrderTaskCard).mock.calls[0]?.[2]
expect(JSON.stringify(submittedRequest)).not.toContain('confirmed_payload')
expect(JSON.stringify(submittedRequest)).not.toContain('rows')
expect(JSON.stringify(submittedRequest)).not.toContain('attachment_ids')
expect(JSON.stringify(submittedRequest)).not.toContain('https://oss.example')
expect(wrapper.text()).toContain(zhCN.status.CONFIRMED)
})
it('resolves Rooming List review cards without submitting row or attachment overrides', async () => {
const detail = createOrderTaskDetail()
useRoomingListBusinessCard(detail, 'REVIEW_REQUIRED')
detail.order_task.order_id = 'order-2001'
detail.order_task.target_resolution_status = 'RESOLVED'
const resolvedDetail = createOrderTaskDetail()
useRoomingListBusinessCard(resolvedDetail, 'PENDING_CONFIRM')
vi.mocked(service.fetchReservationV4OrderTaskDetail).mockResolvedValue(detail)
vi.mocked(service.resolveReservationV4OrderTaskCardReview).mockResolvedValue(resolvedDetail)
const wrapper = await mountWithPlugins(
ReservationV4OrderTaskDetailView,
'/reservation/order-tasks/9001',
)
await flushPromises()
const roomingListSection = wrapper
.findAll('.task-card-section')
.find((section) => section.find('[data-testid="rooming-list-card"]').exists())
expect(roomingListSection).toBeTruthy()
expect(roomingListSection!.find('.review-box').exists()).toBe(true)
expect(roomingListSection!.find('.task-card-section__fields').exists()).toBe(false)
await roomingListSection!.find('textarea').setValue('handled rooming list')
await roomingListSection!.find('button.primary-button').trigger('click')
await flushPromises()
expect(service.confirmReservationV4OrderTaskCard).not.toHaveBeenCalled()
expect(service.resolveReservationV4OrderTaskCardReview).toHaveBeenCalledWith('9001', 'card-rooming-list', {
version: 12,
confirmed_order_id: 'order-2001',
reason: 'handled rooming list',
field_overrides: [],
})
const submittedRequest = vi.mocked(service.resolveReservationV4OrderTaskCardReview).mock.calls[0]?.[2]
expect(JSON.stringify(submittedRequest)).not.toContain('rows')
expect(JSON.stringify(submittedRequest)).not.toContain('attachment_ids')
expect(JSON.stringify(submittedRequest)).not.toContain('https://oss.example')
})
it('renders New Booking Room Information as a business form and confirms stable final values', async () => {
const detail = createOrderTaskDetail({
businessEventType: 'NEW_BOOKING',
@@ -1360,6 +1488,75 @@ function usePaymentBusinessCard(detail: ReservationV4OrderTaskDetailResult): voi
]
}
function useRoomingListBusinessCard(
detail: ReservationV4OrderTaskDetailResult,
cardStatus = 'PENDING_CONFIRM',
): void {
const isPendingConfirm = cardStatus === 'PENDING_CONFIRM'
const isReviewRequired = cardStatus === 'REVIEW_REQUIRED'
detail.business_cards = [
createCard('card-rooming-list', 'ROOMING_LIST', cardStatus, {
version: 12,
availability: createAvailability({
confirmable: isPendingConfirm,
reviewable: isReviewRequired,
editable: false,
read_only: !isPendingConfirm,
}),
display_payload: {
rows: [
{
guest_name: 'ROW_SHOULD_NOT_RENDER',
room_no: '1001',
},
],
attachment_ids: ['rooming-list-att-1'],
attachment_url: 'https://oss.example/private/rooming-list.xlsx',
ai_payload_json: 'AI_PAYLOAD_SHOULD_NOT_RENDER',
pms_import_plan: 'PMS_IMPORT_SHOULD_NOT_RENDER',
ohip_payload: 'OHIP_SHOULD_NOT_RENDER',
},
fields: [
createField('/rooming_list/rows/0/guest_name', {
display_name: 'Rooming list row',
value: 'ROW_SHOULD_NOT_RENDER',
editable: true,
edit_scope: 'CONFIRM',
write_target: 'CONFIRMED_PAYLOAD_JSON',
}),
createField('/rooming_list/attachment_ids', {
display_name: 'attachment_ids',
value: ['rooming-list-att-1'],
editable: true,
edit_scope: 'CONFIRM',
write_target: 'CONFIRMED_PAYLOAD_JSON',
}),
],
}),
]
detail.card_counts = {
...detail.card_counts,
pending_confirm_count: cardStatus === 'PENDING_CONFIRM' ? 2 : 1,
confirmed_count: cardStatus === 'CONFIRMED' ? 1 : 0,
}
}
function useBoundOrder(detail: ReservationV4OrderTaskDetailResult): void {
detail.bound_order = {
order_id: 'order-2001',
hotel_id: 'HOTEL-TEST',
order_status: 'ACTIVE',
temporary_order_no: null,
confirmation_number: null,
group_code: 'GRP-001',
block_code: null,
allotment_code: null,
display_name: 'GRP-001',
created_at: '2026-07-08T03:00:00Z',
updated_at: '2026-07-08T03:00:00Z',
}
}
function createPaymentConversationMedia(): SourceMessageOriginalMedia[] {
return [
{