diff --git a/client/src/components/reservation/ReservationV4PaymentAttachmentPreview.vue b/client/src/components/reservation/ReservationV4PaymentAttachmentPreview.vue index 1ef3c57..ec33fa1 100644 --- a/client/src/components/reservation/ReservationV4PaymentAttachmentPreview.vue +++ b/client/src/components/reservation/ReservationV4PaymentAttachmentPreview.vue @@ -386,7 +386,6 @@ function formatFileSize(size: number | null): string { .payment-attachments { display: grid; gap: 12px; - margin: 0 20px 18px; border: 1px solid var(--th-color-slate-200); border-radius: var(--th-radius-sm); background: var(--th-color-slate-50); diff --git a/client/src/components/reservation/ReservationV4RoomingListCard.vue b/client/src/components/reservation/ReservationV4RoomingListCard.vue index 82b58da..be3f81d 100644 --- a/client/src/components/reservation/ReservationV4RoomingListCard.vue +++ b/client/src/components/reservation/ReservationV4RoomingListCard.vue @@ -58,7 +58,6 @@ function readableValue(value?: string | null): string { .rooming-list-card { display: grid; gap: 12px; - margin: 0 20px 18px; border: 1px solid var(--th-color-slate-200); border-radius: var(--th-radius-sm); background: var(--th-color-slate-50); diff --git a/client/src/components/reservation/ReservationV4TraceCard.vue b/client/src/components/reservation/ReservationV4TraceCard.vue index eb88e3c..3a21522 100644 --- a/client/src/components/reservation/ReservationV4TraceCard.vue +++ b/client/src/components/reservation/ReservationV4TraceCard.vue @@ -415,7 +415,6 @@ function isRecord(value: unknown): value is ReservationRecord { .trace-card { display: grid; gap: 14px; - margin: 0 20px 18px; } .trace-item { diff --git a/client/src/i18n/locales/en-US.ts b/client/src/i18n/locales/en-US.ts index 1e73e35..e9a82cc 100644 --- a/client/src/i18n/locales/en-US.ts +++ b/client/src/i18n/locales/en-US.ts @@ -581,6 +581,7 @@ export default { totalCards: 'Total', pendingCards: 'Pending', reviewCards: 'Review', + readonlyCards: 'View only', confirmedCards: 'Confirmed', orderTaskId: 'Item ID', orderReference: 'Order reference', diff --git a/client/src/i18n/locales/th-TH.ts b/client/src/i18n/locales/th-TH.ts index 6480d8b..4adeb48 100644 --- a/client/src/i18n/locales/th-TH.ts +++ b/client/src/i18n/locales/th-TH.ts @@ -581,6 +581,7 @@ export default { totalCards: 'ทั้งหมด', pendingCards: 'รอยืนยัน', reviewCards: 'รอตรวจสอบ', + readonlyCards: 'ดูอย่างเดียว', confirmedCards: 'ยืนยันแล้ว', orderTaskId: 'รหัสรายการ', orderReference: 'อ้างอิงออเดอร์', diff --git a/client/src/i18n/locales/zh-CN.ts b/client/src/i18n/locales/zh-CN.ts index 8f77d15..70510bc 100644 --- a/client/src/i18n/locales/zh-CN.ts +++ b/client/src/i18n/locales/zh-CN.ts @@ -581,6 +581,7 @@ export default { totalCards: '全部', pendingCards: '待确认', reviewCards: '待复核', + readonlyCards: '仅查看', confirmedCards: '已确认', orderTaskId: '事项编号', orderReference: '订单引用', diff --git a/client/src/tests/reservationV4Views.spec.ts b/client/src/tests/reservationV4Views.spec.ts index ecb0d87..0c5df38 100644 --- a/client/src/tests/reservationV4Views.spec.ts +++ b/client/src/tests/reservationV4Views.spec.ts @@ -7,6 +7,9 @@ import { createI18n } from 'vue-i18n' import { createMemoryHistory, createRouter } from 'vue-router' import taskCardSectionSource from '@/components/reservation/ReservationV4TaskCardSection.vue?raw' +import paymentAttachmentPreviewSource from '@/components/reservation/ReservationV4PaymentAttachmentPreview.vue?raw' +import roomingListCardSource from '@/components/reservation/ReservationV4RoomingListCard.vue?raw' +import traceCardSource from '@/components/reservation/ReservationV4TraceCard.vue?raw' import zhCN from '@/i18n/locales/zh-CN' import { useAuthStore } from '@/stores/authStore' import type { @@ -164,7 +167,11 @@ describe('reservation V4 pages', () => { expect(summary.text()).toContain('3') expect(summary.text()).toContain('2') expect(summary.text()).toContain(zhCN.taskV4.pendingCards) + expect(summary.text()).toContain(zhCN.taskV4.readonlyCards) expect(summary.text()).toContain(zhCN.taskV4.confirmedCards) + const readonlyProgress = summary.find('.handling-progress__item--readonly') + expect(readonlyProgress.exists()).toBe(true) + expect(readonlyProgress.text()).toContain('1') expect(summary.text()).not.toContain('9001') expect(summary.text()).not.toContain(zhCN.taskV4.orderTaskId) }) @@ -201,6 +208,25 @@ describe('reservation V4 pages', () => { expect(wrapper.find('.card-actions .primary-button').exists()).toBe(false) }) + it('keeps bound order links in non-rooming card footers', async () => { + const detail = createOrderTaskDetail() + useBoundOrder(detail) + vi.mocked(service.fetchReservationV4OrderTaskDetail).mockResolvedValue(detail) + + const wrapper = await mountWithPlugins( + ReservationV4OrderTaskDetailView, + '/reservation/order-tasks/9001', + ) + await flushPromises() + + const firstCard = wrapper.find('.task-card-section') + const footerLink = firstCard.find('.task-card-section__footer .card-order-link') + expect(footerLink.exists()).toBe(true) + expect(footerLink.text()).toContain(zhCN.task.viewOrder) + expect(footerLink.attributes('data-to')).toBe('/reservation/orders/order-2001') + expect(firstCard.find('.task-card-section__body .card-order-link').exists()).toBe(false) + }) + it('does not render direct attachment URLs from V4 source message payload', async () => { const detail = createOrderTaskDetail({ sourceDisplayPayload: { @@ -1408,6 +1434,12 @@ describe('reservation V4 pages', () => { expect(taskCardSectionSource).toContain('.task-card-section__footer {\n align-items: flex-end;\n }') }) + it('lets specialized V4 card bodies align with the shared card shell padding', () => { + expect(paymentAttachmentPreviewSource).not.toContain('margin: 0 20px 18px') + expect(roomingListCardSource).not.toContain('margin: 0 20px 18px') + expect(traceCardSource).not.toContain('margin: 0 20px 18px') + }) + it('requires confirmed order id before resolving an unresolved V4 review card', async () => { const detail = createOrderTaskDetail({ businessCardStatus: 'REVIEW_REQUIRED', @@ -1525,7 +1557,8 @@ async function mountWithPlugins(component: object, initialPath: string) { plugins: [pinia, i18n, router, PrimeVue], stubs: { RouterLink: { - template: '', + props: ['to'], + template: '', }, }, }, diff --git a/client/src/views/reservation/ReservationV4OrderTaskDetailView.vue b/client/src/views/reservation/ReservationV4OrderTaskDetailView.vue index 7c59e17..772d8c5 100644 --- a/client/src/views/reservation/ReservationV4OrderTaskDetailView.vue +++ b/client/src/views/reservation/ReservationV4OrderTaskDetailView.vue @@ -80,6 +80,10 @@ {{ t('taskV4.reviewCards') }} {{ detail.card_counts.review_required_count }} +
+ {{ t('taskV4.readonlyCards') }} + {{ detail.card_counts.readonly_count }} +
{{ t('taskV4.confirmedCards') }} {{ detail.card_counts.confirmed_count }} @@ -784,6 +788,10 @@ function formatApiError(error: unknown, fallback: string): { line-height: 1; } +.handling-progress__item--total { + grid-column: 1 / -1; +} + .handling-progress__item--pending strong { color: var(--th-color-info); } @@ -796,6 +804,10 @@ function formatApiError(error: unknown, fallback: string): { color: var(--th-color-success); } +.handling-progress__item--readonly strong { + color: var(--th-color-slate-600); +} + .card-stack { display: grid; gap: 14px; diff --git a/docs/superpowers/plans/2026-07-22-v4-order-task-detail-visual-polish.md b/docs/superpowers/plans/2026-07-22-v4-order-task-detail-visual-polish.md index 3cf26ec..3f72a0f 100644 --- a/docs/superpowers/plans/2026-07-22-v4-order-task-detail-visual-polish.md +++ b/docs/superpowers/plans/2026-07-22-v4-order-task-detail-visual-polish.md @@ -730,4 +730,3 @@ git status --short ``` Expected: modified files are limited to the planned frontend files, locale files, `PROJECT_STATE.md`, and this plan/spec history. `.idea/*` may remain modified as unrelated local changes and must not be staged. -