Files
nullcart/cms/src/components/OrderSummaryPanel.vue
T
2026-08-28 17:31:02 +02:00

187 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<el-card class="order-summary-panel" shadow="never">
<template #header>
<span>Summary</span>
</template>
<el-descriptions :column="1" direction="vertical" class="order-summary-panel__details">
<el-descriptions-item label="Order ID">
<span class="mono text-break">{{ order.id }}</span>
</el-descriptions-item>
<el-descriptions-item label="Access token">
<el-input
class="mono access-token-input"
:model-value="order.accessToken"
type="password"
show-password
readonly
/>
</el-descriptions-item>
<el-descriptions-item label="Created">{{ formatDate(order.createdAt) }}</el-descriptions-item>
<el-descriptions-item v-if="order.failureReason" label="Failure reason">
<span class="failure-reason">{{ formatOrderFailureReason(order.failureReason) }}</span>
</el-descriptions-item>
</el-descriptions>
<div class="order-summary-panel__totals order-totals pt-24">
<div class="flex justify-between gap-12 mb-12">
<span>Subtotal</span>
<span>{{ subtotalFormatted }}</span>
</div>
<p v-if="discounts.length > 0" class="order-totals__section m-0 mb-8">Discounts</p>
<div
v-for="discount in discounts"
:key="discount.id"
class="flex justify-between gap-12 mb-4 order-totals__discount"
>
<span>{{ discount.code }}</span>
<span>{{ formatFiatPrice(discount.amountFiat, fiatCurrency) }}</span>
</div>
<div class="flex justify-between gap-12 mt-12 mb-12">
<span>Total discounts</span>
<span>{{ discountTotalFormatted }}</span>
</div>
<template v-if="hasManualLines">
<div class="flex justify-between gap-12 mt-12 mb-12 order-totals__order-total">
<span>Total</span>
<span>{{ totalFormatted }}</span>
</div>
<div class="flex justify-between gap-12 mb-12">
<span>Shipping</span>
<span>{{ shippingFormatted }}</span>
</div>
<div class="flex justify-between gap-12 order-totals__total pt-8">
<span>Grand total</span>
<span>{{ grandTotalFormatted }}</span>
</div>
</template>
<div v-else class="flex justify-between gap-12 order-totals__total pt-8 mt-12">
<span>Total</span>
<span>{{ totalFormatted }}</span>
</div>
</div>
</el-card>
</template>
<script setup lang="ts">
import { computed, type PropType } from 'vue';
import type { OrderExtended } from '@/types/order/OrderExtended';
import { DeliveryMode } from '@/types/product/DeliveryMode';
import { formatDate } from '@/utils/formatDate';
import { formatFiatPrice } from '@/utils/formatFiatPrice';
import { formatOrderFailureReason } from '@/utils/order/formatOrderFailureReason';
const props = defineProps({
order: {
type: Object as PropType<OrderExtended>,
required: true
}
});
const fiatCurrency = computed(() => props.order.fiatCurrency);
const discounts = computed(() => props.order.discounts ?? []);
const hasManualLines = computed(() =>
(props.order.lines ?? []).some(line => line.deliveryMode === DeliveryMode.Manual)
);
const subtotalFormatted = computed(() => formatFiatPrice(props.order.totals.subtotalFiat, fiatCurrency.value));
const discountTotalFormatted = computed(() =>
formatOrderDiscountTotal(props.order.totals.discountTotalFiat, fiatCurrency.value)
);
const totalFormatted = computed(() => formatFiatPrice(props.order.totals.totalFiat, fiatCurrency.value));
const shippingFormatted = computed(() => {
const shippingAmount = props.order.totals.shippingCostFiat;
if (shippingAmount === null) {
return '—';
}
return formatFiatPrice(shippingAmount, fiatCurrency.value);
});
const grandTotalFormatted = computed(() => {
const grandTotal = props.order.totals.grandTotalFiat ?? props.order.totals.totalFiat;
return formatFiatPrice(grandTotal, fiatCurrency.value);
});
const formatOrderDiscountTotal = (discountTotalFiat: number, currency: string): string => {
if (discountTotalFiat <= 0) {
return formatFiatPrice(0, currency);
}
return `${formatFiatPrice(discountTotalFiat, currency)}`;
};
</script>
<style scoped lang="scss">
.order-summary-panel {
height: 100%;
display: flex;
flex-direction: column;
:deep(.el-card__body) {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
}
.order-summary-panel__details {
flex-shrink: 0;
:deep(.el-descriptions__content) {
min-width: 0;
}
}
.order-summary-panel__totals {
flex-shrink: 0;
margin-top: auto;
}
.access-token-input {
width: 100%;
max-width: min(350px, 100%);
}
.failure-reason {
color: var(--el-color-danger);
}
.order-totals__section {
color: var(--el-text-color-secondary);
}
.order-totals__discount {
color: var(--el-text-color-secondary);
font-size: var(--el-font-size-small);
}
.order-totals__order-total {
font-weight: 600;
}
.order-totals__total {
font-size: var(--el-font-size-large);
font-weight: 600;
border-top: 1px solid var(--el-border-color-lighter);
}
</style>