diff --git a/frontend/e2e/order-history.spec.ts b/frontend/e2e/order-history.spec.ts
index 82c3033..9726ff8 100644
--- a/frontend/e2e/order-history.spec.ts
+++ b/frontend/e2e/order-history.spec.ts
@@ -54,6 +54,26 @@ test.describe('Order history', () => {
await expect(page.getByText('Levererat').first()).toBeVisible()
})
+ test('shows pay button for unpaid order and opens payment page', async ({
+ page,
+ }) => {
+ await page.goto('/logga-in')
+ await page.getByLabel('E-postadress').fill('test@bilhalsning.se')
+ await page.getByLabel('Lösenord').fill('test1234')
+ await page.getByRole('button', { name: 'Logga in' }).click()
+ await page.waitForURL('/')
+
+ await page.goto('/orders')
+
+ const unpaidCard = page.locator('.orders__card', { hasText: 'DEF456' })
+ await expect(unpaidCard.getByRole('link', { name: 'Betala nu' })).toBeVisible()
+ await unpaidCard.getByRole('link', { name: 'Betala nu' }).click()
+
+ await expect(page).toHaveURL(/\/betalning\/c2eebc99/)
+ await expect(page.getByRole('heading', { name: 'Betalning' })).toBeVisible()
+ await expect(page.getByText('DEF456')).toBeVisible()
+ })
+
test('shows tracking links for orders with tracking ID', async ({ page }) => {
await page.goto('/logga-in')
await page.getByLabel('E-postadress').fill('test@bilhalsning.se')
diff --git a/frontend/src/__tests__/OrdersPage.spec.ts b/frontend/src/__tests__/OrdersPage.spec.ts
index e789298..a57a73c 100644
--- a/frontend/src/__tests__/OrdersPage.spec.ts
+++ b/frontend/src/__tests__/OrdersPage.spec.ts
@@ -17,6 +17,11 @@ function createTestRouter() {
history: createMemoryHistory(),
routes: [
{ path: '/orders', name: 'orders', component: OrdersPage },
+ {
+ path: '/betalning/:orderId',
+ name: 'payment',
+ component: { template: '
Payment
' },
+ },
{ path: '/', name: 'home', component: { template: 'Home
' } },
],
})
@@ -38,6 +43,7 @@ const mockOrders = [
{
id: 'c1eebc99-9c0b-4ef8-bb6d-6bb9bd380a11',
plate: 'ABC123',
+ letterText: 'Hej fin bil!',
status: 'sent',
trackingId: 'PN123456789',
createdAt: '2026-05-11T12:00:00Z',
@@ -45,6 +51,7 @@ const mockOrders = [
{
id: 'c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12',
plate: 'DEF456',
+ letterText: 'Vill köpa din bil.',
status: 'pending_payment',
trackingId: null,
createdAt: '2026-05-14T13:00:00Z',
@@ -112,6 +119,7 @@ describe('OrdersPage', () => {
{
id: 'c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12',
plate: 'DEF456',
+ letterText: 'Test',
status: 'pending_payment',
trackingId: null,
createdAt: '2026-05-14T13:00:00Z',
@@ -126,6 +134,16 @@ describe('OrdersPage', () => {
expect(link.exists()).toBe(false)
})
+ it('renders order id and message', async () => {
+ const { wrapper } = mountPage()
+ await new Promise((r) => setTimeout(r, 50))
+ expect(wrapper.text()).toContain('Beställnings-ID')
+ expect(wrapper.text()).toContain('c1eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')
+ expect(wrapper.text()).toContain('Meddelande')
+ expect(wrapper.text()).toContain('Hej fin bil!')
+ expect(wrapper.text()).toContain('Vill köpa din bil.')
+ })
+
it('renders formatted date', async () => {
const { wrapper } = mountPage()
await new Promise((r) => setTimeout(r, 50))
@@ -156,11 +174,35 @@ describe('OrdersPage', () => {
expect(badges[1].classes()).toContain('badge--muted')
})
+ it('shows pay button only for pending payment orders', async () => {
+ const { wrapper } = mountPage()
+ await new Promise((r) => setTimeout(r, 50))
+
+ const payLinks = wrapper.findAll('.orders__pay-btn')
+ expect(payLinks).toHaveLength(1)
+ expect(payLinks[0].text()).toBe('Betala nu')
+
+ const href = payLinks[0].attributes('href')
+ expect(href).toContain('c2eebc99-9c0b-4ef8-bb6d-6bb9bd380a12')
+ expect(href).toContain('plate=DEF456')
+ })
+
+ it('does not show pay button for paid or sent orders', async () => {
+ const { wrapper } = mountPage()
+ await new Promise((r) => setTimeout(r, 50))
+
+ const sentCard = wrapper
+ .findAll('.orders__card')
+ .find((card) => card.text().includes('ABC123'))
+ expect(sentCard?.find('.orders__pay-btn').exists()).toBe(false)
+ })
+
it('renders processing status correctly', async () => {
const ordersWithProcessing = [
{
id: 'c4eebc99-9c0b-4ef8-bb6d-6bb9bd380a14',
plate: 'XYZ123',
+ letterText: 'Processing message',
status: 'processing',
trackingId: null,
createdAt: '2026-05-15T10:00:00Z',
diff --git a/frontend/src/api/orders.ts b/frontend/src/api/orders.ts
index 41dac62..2dd34d8 100644
--- a/frontend/src/api/orders.ts
+++ b/frontend/src/api/orders.ts
@@ -3,6 +3,7 @@ import { request } from './client'
export interface Order {
id: string
plate: string
+ letterText: string
status: string
trackingId: string | null
amountPaid: number | null
diff --git a/frontend/src/pages/OrdersPage.vue b/frontend/src/pages/OrdersPage.vue
index 4b82d7a..43c0bc6 100644
--- a/frontend/src/pages/OrdersPage.vue
+++ b/frontend/src/pages/OrdersPage.vue
@@ -86,6 +86,14 @@ onMounted(async () => {
+ Beställnings-ID
+ {{ order.id }}
+
+ Meddelande
+ {{
+ order.letterText
+ }}
+
Datum
{{
formatDate(order.createdAt)
@@ -103,6 +111,22 @@ onMounted(async () => {
+
+
+
+ Betala nu
+
+
@@ -177,6 +201,17 @@ onMounted(async () => {
color: var(--color-ink);
}
+.orders__order-id {
+ font-family: ui-monospace, monospace;
+ font-size: 0.8125rem;
+ word-break: break-all;
+}
+
+.orders__message {
+ white-space: pre-wrap;
+ line-height: 1.5;
+}
+
.orders__tracking {
font-size: 0.875rem;
color: var(--color-primary);
@@ -188,6 +223,17 @@ onMounted(async () => {
text-decoration: underline;
}
+.orders__card-actions {
+ padding: var(--space-md) var(--space-lg);
+ border-top: 1px solid var(--color-border);
+ background: var(--color-border-light);
+}
+
+.orders__pay-btn {
+ width: 100%;
+ justify-content: center;
+}
+
.orders__empty {
padding: var(--space-2xl) 0;
text-align: center;