refactor(catalog): move box selection into configurator
This commit is contained in:
@@ -4,7 +4,7 @@ import { ClientProductsDocument, type ClientProductsQuery } from '~/composables/
|
|||||||
import { useClientCart } from '~/composables/useClientCart';
|
import { useClientCart } from '~/composables/useClientCart';
|
||||||
|
|
||||||
type ProductNode = ClientProductsQuery['clientProducts'][number];
|
type ProductNode = ClientProductsQuery['clientProducts'][number];
|
||||||
type ParamFieldKey = 'widthMm' | 'lengthM' | 'thicknessMicron' | 'sleeveBrand';
|
type ParamFieldKey = 'widthMm' | 'lengthM' | 'thicknessMicron' | 'sleeveBrand' | 'quantityPerBox';
|
||||||
type ParamValue = number | string;
|
type ParamValue = number | string;
|
||||||
|
|
||||||
type ParsedProduct = ProductNode & {
|
type ParsedProduct = ProductNode & {
|
||||||
@@ -23,15 +23,17 @@ type GroupState = {
|
|||||||
lengthM: number | null;
|
lengthM: number | null;
|
||||||
thicknessMicron: number | null;
|
thicknessMicron: number | null;
|
||||||
sleeveBrand: string | null;
|
sleeveBrand: string | null;
|
||||||
|
quantityPerBox: string | null;
|
||||||
isExpanded: boolean;
|
isExpanded: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PARAM_KEYS: ParamFieldKey[] = ['widthMm', 'lengthM', 'thicknessMicron', 'sleeveBrand'];
|
const PARAM_KEYS: ParamFieldKey[] = ['widthMm', 'lengthM', 'thicknessMicron', 'sleeveBrand', 'quantityPerBox'];
|
||||||
const parameterFields: Array<{ key: ParamFieldKey; label: string }> = [
|
const parameterFields: Array<{ key: ParamFieldKey; label: string }> = [
|
||||||
{ key: 'widthMm', label: 'Ширина' },
|
{ key: 'widthMm', label: 'Ширина' },
|
||||||
{ key: 'lengthM', label: 'Длина' },
|
{ key: 'lengthM', label: 'Длина' },
|
||||||
{ key: 'thicknessMicron', label: 'Толщина' },
|
{ key: 'thicknessMicron', label: 'Толщина' },
|
||||||
{ key: 'sleeveBrand', label: 'Втулка' },
|
{ key: 'sleeveBrand', label: 'Втулка' },
|
||||||
|
{ key: 'quantityPerBox', label: 'Короб' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const coverPresets = [
|
const coverPresets = [
|
||||||
@@ -177,6 +179,13 @@ function getAllFieldOptions(group: ProductGroup, field: ParamFieldKey) {
|
|||||||
const values = new Set<ParamValue>();
|
const values = new Set<ParamValue>();
|
||||||
|
|
||||||
for (const product of group.products) {
|
for (const product of group.products) {
|
||||||
|
if (field === 'quantityPerBox') {
|
||||||
|
for (const option of product.quantityPerBoxOptions) {
|
||||||
|
values.add(option);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const value = product[field];
|
const value = product[field];
|
||||||
if (value !== null && value !== undefined) {
|
if (value !== null && value !== undefined) {
|
||||||
values.add(value);
|
values.add(value);
|
||||||
@@ -202,6 +211,7 @@ function createGroupState(group: ProductGroup): GroupState {
|
|||||||
lengthM: firstProduct?.lengthM ?? null,
|
lengthM: firstProduct?.lengthM ?? null,
|
||||||
thicknessMicron: firstProduct?.thicknessMicron ?? null,
|
thicknessMicron: firstProduct?.thicknessMicron ?? null,
|
||||||
sleeveBrand: firstProduct?.sleeveBrand ?? null,
|
sleeveBrand: firstProduct?.sleeveBrand ?? null,
|
||||||
|
quantityPerBox: firstProduct?.quantityPerBoxOptions[0] ?? null,
|
||||||
isExpanded: false,
|
isExpanded: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -230,7 +240,17 @@ function getGroupState(group: ProductGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function matchesProductState(product: ParsedProduct, state: GroupState, keys: ParamFieldKey[]) {
|
function matchesProductState(product: ParsedProduct, state: GroupState, keys: ParamFieldKey[]) {
|
||||||
return keys.every((key) => state[key] === null || product[key] === state[key]);
|
return keys.every((key) => {
|
||||||
|
if (state[key] === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === 'quantityPerBox') {
|
||||||
|
return product.quantityPerBoxOptions.includes(String(state[key]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return product[key] === state[key];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchingProducts(group: ProductGroup) {
|
function matchingProducts(group: ProductGroup) {
|
||||||
@@ -254,22 +274,6 @@ function selectedProduct(group: ProductGroup) {
|
|||||||
return matches.length === 1 ? matches[0] : null;
|
return matches.length === 1 ? matches[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function boxQuantityLabel(group: ProductGroup) {
|
|
||||||
const product = selectedProduct(group);
|
|
||||||
if (product?.quantityPerBoxOptions.length) {
|
|
||||||
return product.quantityPerBoxOptions.join(' / ');
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = new Set<string>();
|
|
||||||
for (const item of group.products) {
|
|
||||||
for (const option of item.quantityPerBoxOptions) {
|
|
||||||
values.add(option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sortParamValues([...values]).join(' / ') || '—';
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatOptionLabel(field: ParamFieldKey, value: ParamValue) {
|
function formatOptionLabel(field: ParamFieldKey, value: ParamValue) {
|
||||||
if (field === 'widthMm') {
|
if (field === 'widthMm') {
|
||||||
return `${value} мм`;
|
return `${value} мм`;
|
||||||
@@ -311,6 +315,7 @@ function updateField(group: ProductGroup, field: ParamFieldKey, value: ParamValu
|
|||||||
state.lengthM = fallback.lengthM ?? null;
|
state.lengthM = fallback.lengthM ?? null;
|
||||||
state.thicknessMicron = fallback.thicknessMicron ?? null;
|
state.thicknessMicron = fallback.thicknessMicron ?? null;
|
||||||
state.sleeveBrand = fallback.sleeveBrand ?? null;
|
state.sleeveBrand = fallback.sleeveBrand ?? null;
|
||||||
|
state.quantityPerBox = fallback.quantityPerBoxOptions[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function articleLabel(group: ProductGroup) {
|
function articleLabel(group: ProductGroup) {
|
||||||
@@ -439,15 +444,13 @@ function decrementSelected(group: ProductGroup) {
|
|||||||
<aside class="rounded-[28px] bg-base-100 p-4 md:p-5 xl:col-span-1">
|
<aside class="rounded-[28px] bg-base-100 p-4 md:p-5 xl:col-span-1">
|
||||||
<div class="flex h-full flex-col justify-between gap-4">
|
<div class="flex h-full flex-col justify-between gap-4">
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<p class="text-xs font-semibold uppercase tracking-[0.18em] text-base-content/45">Артикул</p>
|
|
||||||
<p class="text-xl font-bold text-[#163624]">{{ articleLabel(group) }}</p>
|
<p class="text-xl font-bold text-[#163624]">{{ articleLabel(group) }}</p>
|
||||||
<p class="text-xs leading-5 text-base-content/65">Короб: {{ boxQuantityLabel(group) }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<button
|
<button
|
||||||
v-if="selectedQty(group) === 0"
|
v-if="selectedQty(group) === 0"
|
||||||
class="btn btn-success h-11 w-full rounded-full border-0 text-sm font-semibold text-success-content"
|
class="btn h-11 w-full rounded-full border-0 bg-[#139957] text-sm font-semibold text-white hover:bg-[#0d854a]"
|
||||||
:disabled="!selectedProduct(group)"
|
:disabled="!selectedProduct(group)"
|
||||||
@click="incrementSelected(group)"
|
@click="incrementSelected(group)"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user