From 88e5c248f4fd532c57a2c608b3aff74635b9c1d4 Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Thu, 2 Apr 2026 18:28:57 +0700 Subject: [PATCH] Move cart flow to catalog add and simplify checkout list --- app/components/orders/OrderStatusBadge.vue | 16 +- app/composables/useClientCart.ts | 109 +++++++++ app/pages/cart.vue | 247 ++++++++------------- app/pages/products.vue | 45 +++- 4 files changed, 261 insertions(+), 156 deletions(-) create mode 100644 app/composables/useClientCart.ts diff --git a/app/components/orders/OrderStatusBadge.vue b/app/components/orders/OrderStatusBadge.vue index 6f74a29..317d385 100644 --- a/app/components/orders/OrderStatusBadge.vue +++ b/app/components/orders/OrderStatusBadge.vue @@ -3,14 +3,28 @@ const props = defineProps<{ status: string; }>(); +const statusLabel = computed(() => { + if (props.status === 'NEW') return 'Уточнение цены'; + if (props.status === 'MANAGER_PROCESSING') return 'В работе у менеджера'; + if (props.status === 'WAITING_DOUBLE_CONFIRM') return 'Ожидает подтверждения'; + if (props.status === 'CONFIRMED') return 'Подтвержден'; + if (props.status === 'IN_PROGRESS') return 'Выполняется'; + if (props.status === 'COMPLETED') return 'Завершен'; + if (props.status === 'CLIENT_REJECTED') return 'Отклонен клиентом'; + if (props.status === 'MANAGER_REJECTED') return 'Отклонен менеджером'; + if (props.status === 'MANAGER_BLOCKED') return 'Заблокирован'; + return props.status; +}); + const className = computed(() => { if (props.status === 'COMPLETED') return 'badge badge-success'; if (props.status === 'CLIENT_REJECTED' || props.status === 'MANAGER_REJECTED') return 'badge badge-error'; if (props.status === 'MANAGER_BLOCKED') return 'badge badge-warning'; + if (props.status === 'NEW') return 'badge badge-warning'; return 'badge badge-info'; }); diff --git a/app/composables/useClientCart.ts b/app/composables/useClientCart.ts new file mode 100644 index 0000000..702b459 --- /dev/null +++ b/app/composables/useClientCart.ts @@ -0,0 +1,109 @@ +export type ClientCartItem = { + productId: string; + productName: string; + sku: string; + isCustomizable: boolean; + quantity: number; + parameters: { + width: number; + thickness: number; + color: string; + }; +}; + +function normalizeQuantity(value: number) { + if (!Number.isFinite(value) || value < 1) { + return 1; + } + + return Math.floor(value); +} + +export function useClientCart() { + const items = useState('client-cart-items', () => []); + + const totalPositions = computed(() => items.value.length); + const totalItems = computed(() => items.value.reduce((sum, item) => sum + item.quantity, 0)); + const totalVolume = computed(() => + items.value.reduce((sum, item) => sum + item.quantity * item.parameters.width * item.parameters.thickness, 0), + ); + + function getQuantity(productId: string) { + return items.value.find((item) => item.productId === productId)?.quantity ?? 0; + } + + function addProduct(product: { + id: string; + name: string; + sku: string; + isCustomizable: boolean; + }) { + const existing = items.value.find((item) => item.productId === product.id); + if (existing) { + existing.quantity += 1; + return; + } + + items.value.push({ + productId: product.id, + productName: product.name, + sku: product.sku, + isCustomizable: product.isCustomizable, + quantity: 1, + parameters: { + width: 100, + thickness: 50, + color: 'прозрачный', + }, + }); + } + + function setQuantity(productId: string, quantity: number) { + const existing = items.value.find((item) => item.productId === productId); + if (!existing) { + return; + } + + existing.quantity = normalizeQuantity(quantity); + } + + function incrementQuantity(productId: string) { + const existing = items.value.find((item) => item.productId === productId); + if (!existing) { + return; + } + + existing.quantity += 1; + } + + function decrementQuantity(productId: string) { + const existing = items.value.find((item) => item.productId === productId); + if (!existing) { + return; + } + + existing.quantity = normalizeQuantity(existing.quantity - 1); + } + + function removeProduct(productId: string) { + items.value = items.value.filter((item) => item.productId !== productId); + } + + function clearCart() { + items.value = []; + } + + return { + items, + totalPositions, + totalItems, + totalVolume, + addProduct, + setQuantity, + incrementQuantity, + decrementQuantity, + removeProduct, + clearCart, + getQuantity, + }; +} diff --git a/app/pages/cart.vue b/app/pages/cart.vue index 7384116..c0b3692 100644 --- a/app/pages/cart.vue +++ b/app/pages/cart.vue @@ -1,70 +1,41 @@ @@ -117,108 +88,76 @@ async function submitCart() {

Корзина

-
-
-
-
- Проверяем карточку контрагента... -
-
- Для оформления заявки заполните карточку контрагента в - профиле. -
+
+
+ Проверяем карточку контрагента... +
+
+ Для оформления заявки заполните карточку контрагента в + профиле. +
-
-

Добавить позицию

-
- +
+

Список позиций

- - - - - - - -
- - -
- -
-

Список позиций

- -
- Корзина пока пустая. -
- -
    -
  • -
    -

    {{ item.productName }}

    -

    - {{ item.quantity }} шт. • {{ item.width }} × {{ item.thickness }} • {{ item.color }} -

    -
    - -
    - Объем: {{ lineVolume(item) }} - -
    -
  • -
-
+
+ Корзина пока пустая. Добавьте товар из каталога.
- -
-
+
+

{{ item.productName }}

+

SKU: {{ item.sku }}

+

+ Объем: {{ lineVolume(item.productId) }} +

+
-
{{ success }}
-
{{ errorMessage }}
+
+ + {{ item.quantity }} + + +
+ + +
+ +
+

Итого

+
    +
  • + Позиций + {{ cart.totalPositions }} +
  • +
  • + Количество, шт. + {{ cart.totalItems }} +
  • +
  • + Суммарный объем + {{ cart.totalVolume }} +
  • +
+
+ + + +
{{ success }}
+
{{ errorMessage }}
+
diff --git a/app/pages/products.vue b/app/pages/products.vue index 36a527b..847debb 100644 --- a/app/pages/products.vue +++ b/app/pages/products.vue @@ -1,10 +1,12 @@