Back cart with GraphQL storage
This commit is contained in:
@@ -1,14 +1,27 @@
|
||||
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: {
|
||||
width: number;
|
||||
thickness: number;
|
||||
color: string;
|
||||
};
|
||||
parameters: CartParameters;
|
||||
};
|
||||
|
||||
function normalizeQuantity(value: number) {
|
||||
@@ -19,8 +32,33 @@ function normalizeQuantity(value: number) {
|
||||
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));
|
||||
@@ -28,94 +66,138 @@ export function useClientCart() {
|
||||
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;
|
||||
}
|
||||
|
||||
function addProduct(product: {
|
||||
async 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: 'прозрачный',
|
||||
},
|
||||
const { data } = await client.mutate({
|
||||
mutation: AddProductToCartDocument,
|
||||
variables: { productId: product.id },
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
if (data?.addProductToCart) {
|
||||
applyCart(data.addProductToCart);
|
||||
}
|
||||
}
|
||||
|
||||
function setQuantity(productId: string, quantity: number) {
|
||||
const existing = items.value.find((item) => item.productId === productId);
|
||||
if (!existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
async function setQuantity(productId: string, quantity: number) {
|
||||
const normalizedQuantity = normalizeQuantity(quantity);
|
||||
if (normalizedQuantity === 0) {
|
||||
removeProduct(productId);
|
||||
return;
|
||||
}
|
||||
const { data } = await client.mutate({
|
||||
mutation: UpdateCartItemQuantityDocument,
|
||||
variables: {
|
||||
input: {
|
||||
productId,
|
||||
quantity: normalizedQuantity,
|
||||
},
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
existing.quantity = normalizedQuantity;
|
||||
if (data?.updateCartItemQuantity) {
|
||||
applyCart(data.updateCartItemQuantity);
|
||||
}
|
||||
}
|
||||
|
||||
function incrementQuantity(productId: string) {
|
||||
const existing = items.value.find((item) => item.productId === productId);
|
||||
if (!existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
existing.quantity += 1;
|
||||
async function incrementQuantity(productId: string) {
|
||||
await setQuantity(productId, getQuantity(productId) + 1);
|
||||
}
|
||||
|
||||
function decrementQuantity(productId: string) {
|
||||
const existing = items.value.find((item) => item.productId === productId);
|
||||
if (!existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedQuantity = normalizeQuantity(existing.quantity - 1);
|
||||
if (normalizedQuantity === 0) {
|
||||
removeProduct(productId);
|
||||
return;
|
||||
}
|
||||
|
||||
existing.quantity = normalizedQuantity;
|
||||
async function decrementQuantity(productId: string) {
|
||||
await setQuantity(productId, getQuantity(productId) - 1);
|
||||
}
|
||||
|
||||
function removeProduct(productId: string) {
|
||||
items.value = items.value.filter((item) => item.productId !== productId);
|
||||
async function removeProduct(productId: string) {
|
||||
const { data } = await client.mutate({
|
||||
mutation: RemoveCartItemDocument,
|
||||
variables: { productId },
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
if (data?.removeCartItem) {
|
||||
applyCart(data.removeCartItem);
|
||||
}
|
||||
}
|
||||
|
||||
function clearCart() {
|
||||
items.value = [];
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user