204 lines
5.0 KiB
TypeScript
204 lines
5.0 KiB
TypeScript
import {
|
|
AddProductToCartDocument,
|
|
ClearCartDocument,
|
|
MyCartDocument,
|
|
RemoveCartItemDocument,
|
|
SetCartDeliveryAddressDocument,
|
|
UpdateCartItemQuantityDocument,
|
|
type MyCartQuery,
|
|
} from '~/composables/graphql/generated';
|
|
import { useGqlClient } from '~/composables/useGqlClient';
|
|
|
|
type CartParameters = {
|
|
width: number;
|
|
thickness: number;
|
|
color: string;
|
|
};
|
|
|
|
export type ClientCartItem = {
|
|
productId: string;
|
|
productName: string;
|
|
sku: string;
|
|
isCustomizable: boolean;
|
|
quantity: number;
|
|
parameters: CartParameters;
|
|
};
|
|
|
|
function normalizeQuantity(value: number) {
|
|
if (!Number.isFinite(value) || value < 0) {
|
|
return 0;
|
|
}
|
|
|
|
return Math.floor(value);
|
|
}
|
|
|
|
function normalizeParameters(value: unknown): CartParameters {
|
|
const source = (value && typeof value === 'object') ? value as Partial<CartParameters> : {};
|
|
|
|
return {
|
|
width: Number(source.width ?? 100),
|
|
thickness: Number(source.thickness ?? 50),
|
|
color: typeof source.color === 'string' ? source.color : 'прозрачный',
|
|
};
|
|
}
|
|
|
|
function mapCartItem(item: MyCartQuery['myCart']['items'][number]): ClientCartItem {
|
|
return {
|
|
productId: item.productId,
|
|
productName: item.productName,
|
|
sku: item.sku,
|
|
isCustomizable: item.isCustomizable,
|
|
quantity: Number(item.quantity),
|
|
parameters: normalizeParameters(item.parameters),
|
|
};
|
|
}
|
|
|
|
export function useClientCart() {
|
|
const client = useGqlClient();
|
|
const items = useState<ClientCartItem[]>('client-cart-items', () => []);
|
|
const selectedDeliveryAddressId = useState<string>('client-cart-delivery-address-id', () => '');
|
|
const initialized = useState<boolean>('client-cart-initialized', () => false);
|
|
const loading = useState<boolean>('client-cart-loading', () => false);
|
|
|
|
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 applyCart(cart: MyCartQuery['myCart']) {
|
|
items.value = cart.items.map(mapCartItem);
|
|
selectedDeliveryAddressId.value = cart.deliveryAddressId ?? '';
|
|
initialized.value = true;
|
|
}
|
|
|
|
async function fetchCart(force = false) {
|
|
if (loading.value) {
|
|
return;
|
|
}
|
|
|
|
if (initialized.value && !force) {
|
|
return;
|
|
}
|
|
|
|
loading.value = true;
|
|
|
|
try {
|
|
const { data } = await client.query({
|
|
query: MyCartDocument,
|
|
fetchPolicy: 'network-only',
|
|
});
|
|
|
|
if (data?.myCart) {
|
|
applyCart(data.myCart);
|
|
}
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
function getQuantity(productId: string) {
|
|
return items.value.find((item) => item.productId === productId)?.quantity ?? 0;
|
|
}
|
|
|
|
async function addProduct(product: {
|
|
id: string;
|
|
name: string;
|
|
sku: string;
|
|
isCustomizable: boolean;
|
|
}) {
|
|
const { data } = await client.mutate({
|
|
mutation: AddProductToCartDocument,
|
|
variables: { productId: product.id },
|
|
fetchPolicy: 'no-cache',
|
|
});
|
|
|
|
if (data?.addProductToCart) {
|
|
applyCart(data.addProductToCart);
|
|
}
|
|
}
|
|
|
|
async function setQuantity(productId: string, quantity: number) {
|
|
const normalizedQuantity = normalizeQuantity(quantity);
|
|
const { data } = await client.mutate({
|
|
mutation: UpdateCartItemQuantityDocument,
|
|
variables: {
|
|
input: {
|
|
productId,
|
|
quantity: normalizedQuantity,
|
|
},
|
|
},
|
|
fetchPolicy: 'no-cache',
|
|
});
|
|
|
|
if (data?.updateCartItemQuantity) {
|
|
applyCart(data.updateCartItemQuantity);
|
|
}
|
|
}
|
|
|
|
async function incrementQuantity(productId: string) {
|
|
await setQuantity(productId, getQuantity(productId) + 1);
|
|
}
|
|
|
|
async function decrementQuantity(productId: string) {
|
|
await setQuantity(productId, getQuantity(productId) - 1);
|
|
}
|
|
|
|
async function removeProduct(productId: string) {
|
|
const { data } = await client.mutate({
|
|
mutation: RemoveCartItemDocument,
|
|
variables: { productId },
|
|
fetchPolicy: 'no-cache',
|
|
});
|
|
|
|
if (data?.removeCartItem) {
|
|
applyCart(data.removeCartItem);
|
|
}
|
|
}
|
|
|
|
async function setDeliveryAddress(addressId: string | null) {
|
|
const { data } = await client.mutate({
|
|
mutation: SetCartDeliveryAddressDocument,
|
|
variables: { addressId: addressId || null },
|
|
fetchPolicy: 'no-cache',
|
|
});
|
|
|
|
if (data?.setCartDeliveryAddress) {
|
|
applyCart(data.setCartDeliveryAddress);
|
|
}
|
|
}
|
|
|
|
async function clearCart() {
|
|
const { data } = await client.mutate({
|
|
mutation: ClearCartDocument,
|
|
fetchPolicy: 'no-cache',
|
|
});
|
|
|
|
if (data?.clearCart) {
|
|
applyCart(data.clearCart);
|
|
}
|
|
}
|
|
|
|
if (!initialized.value && !loading.value) {
|
|
void fetchCart();
|
|
}
|
|
|
|
return {
|
|
items,
|
|
loading,
|
|
selectedDeliveryAddressId,
|
|
totalPositions,
|
|
totalItems,
|
|
totalVolume,
|
|
fetchCart,
|
|
addProduct,
|
|
setQuantity,
|
|
incrementQuantity,
|
|
decrementQuantity,
|
|
removeProduct,
|
|
clearCart,
|
|
setDeliveryAddress,
|
|
getQuantity,
|
|
};
|
|
}
|