Files
web-frontend/app/composables/useClientCart.ts
2026-04-04 09:08:51 +07:00

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,
};
}