Unify order items layout across cart and orders
This commit is contained in:
@@ -60,15 +60,6 @@ onMounted(() => {
|
||||
void fetchCart(true);
|
||||
});
|
||||
|
||||
function lineVolume(productId: string) {
|
||||
const item = cartItems.value.find((entry) => entry.productId === productId);
|
||||
if (!item) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Number(item.quantity) * Number(item.parameters.width) * Number(item.parameters.thickness);
|
||||
}
|
||||
|
||||
function increment(productId: string) {
|
||||
success.value = '';
|
||||
errorMessage.value = '';
|
||||
@@ -197,30 +188,22 @@ async function submitCart() {
|
||||
Корзина пока пустая. Добавьте товар из каталога.
|
||||
</div>
|
||||
|
||||
<ul v-else class="space-y-3">
|
||||
<li
|
||||
v-for="item in cartItems"
|
||||
:key="item.productId"
|
||||
class="surface-card flex flex-col gap-3 rounded-3xl px-4 py-4 md:flex-row md:items-center md:justify-between md:px-5 md:py-5"
|
||||
>
|
||||
<div>
|
||||
<p class="font-semibold text-[#123824]">{{ item.productName }}</p>
|
||||
<p class="text-xs opacity-70">SKU: {{ item.sku }}</p>
|
||||
<p class="text-sm opacity-80">
|
||||
Объем: {{ lineVolume(item.productId) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<button class="btn btn-square btn-sm" @click="decrement(item.productId)">-</button>
|
||||
<span class="min-w-8 text-center font-semibold">{{ item.quantity }}</span>
|
||||
<button class="btn btn-square btn-sm" @click="increment(item.productId)">+</button>
|
||||
<button class="btn btn-ghost btn-sm text-error" @click="removeFromCart(item.productId)">
|
||||
Удалить
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<OrdersOrderItemsTable
|
||||
v-else
|
||||
mode="cart"
|
||||
:items="cartItems.map((item) => ({
|
||||
id: item.productId,
|
||||
productName: item.productName,
|
||||
sku: item.sku,
|
||||
quantity: item.quantity,
|
||||
parameters: item.parameters,
|
||||
unitPrice: null,
|
||||
lineTotal: null,
|
||||
}))"
|
||||
@increment="increment"
|
||||
@decrement="decrement"
|
||||
@remove="removeFromCart"
|
||||
/>
|
||||
|
||||
<div class="divider my-1" />
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
formatPrice,
|
||||
orderLogisticsStateText,
|
||||
orderDeliveryStateText,
|
||||
orderLineStateText,
|
||||
} from '~/composables/useOrderDetailPresentation';
|
||||
|
||||
definePageMeta({
|
||||
@@ -76,19 +75,6 @@ function parseMoneyDraft(value: string) {
|
||||
return Math.round((normalized + Number.EPSILON) * 100) / 100;
|
||||
}
|
||||
|
||||
function draftUnitPrice(itemId: string, fallback?: number | null) {
|
||||
return parseMoneyDraft(itemPriceDrafts[itemId] ?? '') ?? fallback ?? null;
|
||||
}
|
||||
|
||||
function draftLineTotal(item: ManagerOrderItem['items'][number]) {
|
||||
const unitPrice = draftUnitPrice(item.id, item.unitPrice);
|
||||
if (unitPrice == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Math.round((unitPrice * item.quantity + Number.EPSILON) * 100) / 100;
|
||||
}
|
||||
|
||||
const draftDeliveryTerms = computed(() => deliveryTermsDraft.value.trim() || currentOrder.value?.deliveryTerms || null);
|
||||
const draftDeliveryFee = computed(() => parseMoneyDraft(deliveryFeeDraft.value) ?? currentOrder.value?.deliveryFee ?? null);
|
||||
const canEditOffer = computed(() => (
|
||||
@@ -233,29 +219,17 @@ watch(
|
||||
audience="manager"
|
||||
/>
|
||||
|
||||
<div class="surface-card rounded-3xl p-5">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-[#123824]">Состав заказа</h2>
|
||||
<ul class="mt-4 space-y-3">
|
||||
<li v-for="item in currentOrder.items" :key="item.id" class="manager-mini-card space-y-3">
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-semibold text-[#123824]">{{ item.productName }} × {{ item.quantity }}</p>
|
||||
<p class="text-sm text-[#5c7b69]">{{ orderLineStateText(draftUnitPrice(item.id, item.unitPrice), draftLineTotal(item)) }}</p>
|
||||
</div>
|
||||
|
||||
<label class="form-control">
|
||||
<span class="mb-2 text-xs font-semibold uppercase tracking-[0.22em] text-[#6a8a76]">Цена за единицу</span>
|
||||
<input
|
||||
v-model="itemPriceDrafts[item.id]"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.01"
|
||||
placeholder="Например, 125.50"
|
||||
class="input input-bordered w-full rounded-2xl bg-white"
|
||||
:disabled="!canEditOffer"
|
||||
>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<OrdersOrderItemsTable
|
||||
class="mt-4"
|
||||
:items="currentOrder.items"
|
||||
:calculation-payload="currentOrder.calculationPayload"
|
||||
:editable="true"
|
||||
:unit-price-drafts="itemPriceDrafts"
|
||||
:disabled="!canEditOffer"
|
||||
@update:unit-price="({ itemId, value }) => { itemPriceDrafts[itemId] = value; }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="surface-card rounded-3xl p-5">
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
type OrderDetailQuery,
|
||||
} from '~/composables/graphql/generated';
|
||||
import {
|
||||
orderLogisticsStateText,
|
||||
orderDeliveryStateText,
|
||||
orderLineStateText,
|
||||
orderLogisticsStateText,
|
||||
} from '~/composables/useOrderDetailPresentation';
|
||||
|
||||
type OrderItem = NonNullable<OrderDetailQuery['order']>;
|
||||
@@ -48,18 +47,13 @@ const currentOrder = computed<OrderItem | null>(() =>
|
||||
audience="client"
|
||||
/>
|
||||
|
||||
<div class="surface-card rounded-3xl p-5">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-[#123824]">Состав заказа</h2>
|
||||
<ul class="mt-4 space-y-3">
|
||||
<li
|
||||
v-for="item in currentOrder.items"
|
||||
:key="item.id"
|
||||
class="manager-mini-card space-y-2"
|
||||
>
|
||||
<p class="text-sm font-semibold text-[#123824]">{{ item.productName }} × {{ item.quantity }}</p>
|
||||
<p class="text-sm text-[#5c7b69]">{{ orderLineStateText(item.unitPrice, item.lineTotal) }}</p>
|
||||
</li>
|
||||
</ul>
|
||||
<OrdersOrderItemsTable
|
||||
class="mt-4"
|
||||
:items="currentOrder.items"
|
||||
:calculation-payload="currentOrder.calculationPayload"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="surface-card rounded-3xl p-5">
|
||||
|
||||
Reference in New Issue
Block a user