Back cart with GraphQL storage

This commit is contained in:
Ruslan Bakiev
2026-04-04 09:08:51 +07:00
parent f1ee0850c9
commit 264b88bcee
10 changed files with 644 additions and 68 deletions

View File

@@ -62,6 +62,30 @@ export type BonusTransaction = {
userId: Scalars['ID']['output'];
};
export type Cart = {
__typename?: 'Cart';
createdAt: Scalars['DateTime']['output'];
deliveryAddress?: Maybe<DeliveryAddress>;
deliveryAddressId?: Maybe<Scalars['ID']['output']>;
id: Scalars['ID']['output'];
items: Array<CartItem>;
updatedAt: Scalars['DateTime']['output'];
userId: Scalars['ID']['output'];
};
export type CartItem = {
__typename?: 'CartItem';
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
isCustomizable: Scalars['Boolean']['output'];
parameters: Scalars['JSON']['output'];
productId: Scalars['ID']['output'];
productName: Scalars['String']['output'];
quantity: Scalars['Float']['output'];
sku: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
};
export type Company = {
__typename?: 'Company';
id: Scalars['ID']['output'];
@@ -180,7 +204,9 @@ export type Mutation = {
__typename?: 'Mutation';
acceptInvitation: User;
addBonusTransaction: BonusTransaction;
addProductToCart: Cart;
blockOrder: Order;
clearCart: Cart;
clientReviewOrder: Order;
completeOrder: Order;
connectMessenger: MessengerConnection;
@@ -192,15 +218,18 @@ export type Mutation = {
managerFinalizeOrder: Order;
managerSetOrderOffer: Order;
registerSelf: RegistrationRequest;
removeCartItem: Cart;
requestLoginCode: AuthCodeRequestResult;
requestRewardWithdrawal: RewardWithdrawalRequest;
reviewRegistrationRequest: RegistrationRequest;
reviewRewardWithdrawal: RewardWithdrawalRequest;
sendTestMessengerMessage: MessengerDispatchResult;
setCartDeliveryAddress: Cart;
setMyDefaultDeliveryAddress: DeliveryAddress;
startOrderWork: Order;
submitCalculationOrder: Order;
submitReadyOrder: Order;
updateCartItemQuantity: Cart;
upsertMyCounterpartyProfile: CounterpartyProfile;
verifyLoginCode: AuthSession;
};
@@ -216,6 +245,11 @@ export type MutationAddBonusTransactionArgs = {
};
export type MutationAddProductToCartArgs = {
productId: Scalars['ID']['input'];
};
export type MutationBlockOrderArgs = {
input: BlockOrderInput;
};
@@ -278,6 +312,11 @@ export type MutationRegisterSelfArgs = {
};
export type MutationRemoveCartItemArgs = {
productId: Scalars['ID']['input'];
};
export type MutationRequestLoginCodeArgs = {
input: RequestLoginCodeInput;
};
@@ -305,6 +344,11 @@ export type MutationSendTestMessengerMessageArgs = {
};
export type MutationSetCartDeliveryAddressArgs = {
addressId?: InputMaybe<Scalars['ID']['input']>;
};
export type MutationSetMyDefaultDeliveryAddressArgs = {
addressId: Scalars['ID']['input'];
};
@@ -325,6 +369,11 @@ export type MutationSubmitReadyOrderArgs = {
};
export type MutationUpdateCartItemQuantityArgs = {
input: UpdateCartItemQuantityInput;
};
export type MutationUpsertMyCounterpartyProfileArgs = {
input: UpsertMyCounterpartyProfileInput;
};
@@ -430,6 +479,7 @@ export type Query = {
managerNotificationHistory: Array<NotificationHistoryItem>;
managerOrders: Array<Order>;
me?: Maybe<User>;
myCart: Cart;
myCounterpartyProfile?: Maybe<CounterpartyProfile>;
myCurrentOrders: Array<Order>;
myDeliveryAddresses: Array<DeliveryAddress>;
@@ -564,6 +614,11 @@ export type SubmitReadyOrderInput = {
items: Array<ReadyOrderItemInput>;
};
export type UpdateCartItemQuantityInput = {
productId: Scalars['ID']['input'];
quantity: Scalars['Float']['input'];
};
export type UpsertMyCounterpartyProfileInput = {
bankName: Scalars['String']['input'];
bik: Scalars['String']['input'];
@@ -645,6 +700,44 @@ export type VerifyLoginCodeMutationVariables = Exact<{
export type VerifyLoginCodeMutation = { __typename?: 'Mutation', verifyLoginCode: { __typename?: 'AuthSession', accessToken: string, expiresAt: any, user: { __typename?: 'User', id: string, email: string, fullName: string, role: UserRole, company?: { __typename?: 'Company', id: string } | null } } };
export type AddProductToCartMutationVariables = Exact<{
productId: Scalars['ID']['input'];
}>;
export type AddProductToCartMutation = { __typename?: 'Mutation', addProductToCart: { __typename?: 'Cart', id: string, userId: string, deliveryAddressId?: string | null, updatedAt: any, items: Array<{ __typename?: 'CartItem', id: string, productId: string, productName: string, sku: string, isCustomizable: boolean, quantity: number, parameters: any, updatedAt: any }> } };
export type ClearCartMutationVariables = Exact<{ [key: string]: never; }>;
export type ClearCartMutation = { __typename?: 'Mutation', clearCart: { __typename?: 'Cart', id: string, userId: string, deliveryAddressId?: string | null, updatedAt: any, items: Array<{ __typename?: 'CartItem', id: string, productId: string, productName: string, sku: string, isCustomizable: boolean, quantity: number, parameters: any, updatedAt: any }> } };
export type MyCartQueryVariables = Exact<{ [key: string]: never; }>;
export type MyCartQuery = { __typename?: 'Query', myCart: { __typename?: 'Cart', id: string, userId: string, deliveryAddressId?: string | null, updatedAt: any, items: Array<{ __typename?: 'CartItem', id: string, productId: string, productName: string, sku: string, isCustomizable: boolean, quantity: number, parameters: any, updatedAt: any }> } };
export type RemoveCartItemMutationVariables = Exact<{
productId: Scalars['ID']['input'];
}>;
export type RemoveCartItemMutation = { __typename?: 'Mutation', removeCartItem: { __typename?: 'Cart', id: string, userId: string, deliveryAddressId?: string | null, updatedAt: any, items: Array<{ __typename?: 'CartItem', id: string, productId: string, productName: string, sku: string, isCustomizable: boolean, quantity: number, parameters: any, updatedAt: any }> } };
export type SetCartDeliveryAddressMutationVariables = Exact<{
addressId?: InputMaybe<Scalars['ID']['input']>;
}>;
export type SetCartDeliveryAddressMutation = { __typename?: 'Mutation', setCartDeliveryAddress: { __typename?: 'Cart', id: string, userId: string, deliveryAddressId?: string | null, updatedAt: any, items: Array<{ __typename?: 'CartItem', id: string, productId: string, productName: string, sku: string, isCustomizable: boolean, quantity: number, parameters: any, updatedAt: any }> } };
export type UpdateCartItemQuantityMutationVariables = Exact<{
input: UpdateCartItemQuantityInput;
}>;
export type UpdateCartItemQuantityMutation = { __typename?: 'Mutation', updateCartItemQuantity: { __typename?: 'Cart', id: string, userId: string, deliveryAddressId?: string | null, updatedAt: any, items: Array<{ __typename?: 'CartItem', id: string, productId: string, productName: string, sku: string, isCustomizable: boolean, quantity: number, parameters: any, updatedAt: any }> } };
export type ClientProductsQueryVariables = Exact<{ [key: string]: never; }>;
@@ -1015,6 +1108,252 @@ export function useVerifyLoginCodeMutation(options: VueApolloComposable.UseMutat
return VueApolloComposable.useMutation<VerifyLoginCodeMutation, VerifyLoginCodeMutationVariables>(VerifyLoginCodeDocument, options);
}
export type VerifyLoginCodeMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<VerifyLoginCodeMutation, VerifyLoginCodeMutationVariables>;
export const AddProductToCartDocument = gql`
mutation AddProductToCart($productId: ID!) {
addProductToCart(productId: $productId) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}
`;
/**
* __useAddProductToCartMutation__
*
* To run a mutation, you first call `useAddProductToCartMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useAddProductToCartMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useAddProductToCartMutation({
* variables: {
* productId: // value for 'productId'
* },
* });
*/
export function useAddProductToCartMutation(options: VueApolloComposable.UseMutationOptions<AddProductToCartMutation, AddProductToCartMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<AddProductToCartMutation, AddProductToCartMutationVariables>> = {}) {
return VueApolloComposable.useMutation<AddProductToCartMutation, AddProductToCartMutationVariables>(AddProductToCartDocument, options);
}
export type AddProductToCartMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<AddProductToCartMutation, AddProductToCartMutationVariables>;
export const ClearCartDocument = gql`
mutation ClearCart {
clearCart {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}
`;
/**
* __useClearCartMutation__
*
* To run a mutation, you first call `useClearCartMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useClearCartMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useClearCartMutation();
*/
export function useClearCartMutation(options: VueApolloComposable.UseMutationOptions<ClearCartMutation, ClearCartMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<ClearCartMutation, ClearCartMutationVariables>> = {}) {
return VueApolloComposable.useMutation<ClearCartMutation, ClearCartMutationVariables>(ClearCartDocument, options);
}
export type ClearCartMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<ClearCartMutation, ClearCartMutationVariables>;
export const MyCartDocument = gql`
query MyCart {
myCart {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}
`;
/**
* __useMyCartQuery__
*
* To run a query within a Vue component, call `useMyCartQuery` and pass it any options that fit your needs.
* When your component renders, `useMyCartQuery` returns an object from Apollo Client that contains result, loading and error properties
* you can use to render your UI.
*
* @param options that will be passed into the query, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/query.html#options;
*
* @example
* const { result, loading, error } = useMyCartQuery();
*/
export function useMyCartQuery(options: VueApolloComposable.UseQueryOptions<MyCartQuery, MyCartQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<MyCartQuery, MyCartQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<MyCartQuery, MyCartQueryVariables>> = {}) {
return VueApolloComposable.useQuery<MyCartQuery, MyCartQueryVariables>(MyCartDocument, {}, options);
}
export function useMyCartLazyQuery(options: VueApolloComposable.UseQueryOptions<MyCartQuery, MyCartQueryVariables> | VueCompositionApi.Ref<VueApolloComposable.UseQueryOptions<MyCartQuery, MyCartQueryVariables>> | ReactiveFunction<VueApolloComposable.UseQueryOptions<MyCartQuery, MyCartQueryVariables>> = {}) {
return VueApolloComposable.useLazyQuery<MyCartQuery, MyCartQueryVariables>(MyCartDocument, {}, options);
}
export type MyCartQueryCompositionFunctionResult = VueApolloComposable.UseQueryReturn<MyCartQuery, MyCartQueryVariables>;
export const RemoveCartItemDocument = gql`
mutation RemoveCartItem($productId: ID!) {
removeCartItem(productId: $productId) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}
`;
/**
* __useRemoveCartItemMutation__
*
* To run a mutation, you first call `useRemoveCartItemMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useRemoveCartItemMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useRemoveCartItemMutation({
* variables: {
* productId: // value for 'productId'
* },
* });
*/
export function useRemoveCartItemMutation(options: VueApolloComposable.UseMutationOptions<RemoveCartItemMutation, RemoveCartItemMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<RemoveCartItemMutation, RemoveCartItemMutationVariables>> = {}) {
return VueApolloComposable.useMutation<RemoveCartItemMutation, RemoveCartItemMutationVariables>(RemoveCartItemDocument, options);
}
export type RemoveCartItemMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<RemoveCartItemMutation, RemoveCartItemMutationVariables>;
export const SetCartDeliveryAddressDocument = gql`
mutation SetCartDeliveryAddress($addressId: ID) {
setCartDeliveryAddress(addressId: $addressId) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}
`;
/**
* __useSetCartDeliveryAddressMutation__
*
* To run a mutation, you first call `useSetCartDeliveryAddressMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useSetCartDeliveryAddressMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useSetCartDeliveryAddressMutation({
* variables: {
* addressId: // value for 'addressId'
* },
* });
*/
export function useSetCartDeliveryAddressMutation(options: VueApolloComposable.UseMutationOptions<SetCartDeliveryAddressMutation, SetCartDeliveryAddressMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<SetCartDeliveryAddressMutation, SetCartDeliveryAddressMutationVariables>> = {}) {
return VueApolloComposable.useMutation<SetCartDeliveryAddressMutation, SetCartDeliveryAddressMutationVariables>(SetCartDeliveryAddressDocument, options);
}
export type SetCartDeliveryAddressMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<SetCartDeliveryAddressMutation, SetCartDeliveryAddressMutationVariables>;
export const UpdateCartItemQuantityDocument = gql`
mutation UpdateCartItemQuantity($input: UpdateCartItemQuantityInput!) {
updateCartItemQuantity(input: $input) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}
`;
/**
* __useUpdateCartItemQuantityMutation__
*
* To run a mutation, you first call `useUpdateCartItemQuantityMutation` within a Vue component and pass it any options that fit your needs.
* When your component renders, `useUpdateCartItemQuantityMutation` returns an object that includes:
* - A mutate function that you can call at any time to execute the mutation
* - Several other properties: https://v4.apollo.vuejs.org/api/use-mutation.html#return
*
* @param options that will be passed into the mutation, supported options are listed on: https://v4.apollo.vuejs.org/guide-composable/mutation.html#options;
*
* @example
* const { mutate, loading, error, onDone } = useUpdateCartItemQuantityMutation({
* variables: {
* input: // value for 'input'
* },
* });
*/
export function useUpdateCartItemQuantityMutation(options: VueApolloComposable.UseMutationOptions<UpdateCartItemQuantityMutation, UpdateCartItemQuantityMutationVariables> | ReactiveFunction<VueApolloComposable.UseMutationOptions<UpdateCartItemQuantityMutation, UpdateCartItemQuantityMutationVariables>> = {}) {
return VueApolloComposable.useMutation<UpdateCartItemQuantityMutation, UpdateCartItemQuantityMutationVariables>(UpdateCartItemQuantityDocument, options);
}
export type UpdateCartItemQuantityMutationCompositionFunctionResult = VueApolloComposable.UseMutationReturn<UpdateCartItemQuantityMutation, UpdateCartItemQuantityMutationVariables>;
export const ClientProductsDocument = gql`
query ClientProducts {
clientProducts {

View File

@@ -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',
});
if (data?.updateCartItemQuantity) {
applyCart(data.updateCartItemQuantity);
}
}
existing.quantity = normalizedQuantity;
async function incrementQuantity(productId: string) {
await setQuantity(productId, getQuantity(productId) + 1);
}
function incrementQuantity(productId: string) {
const existing = items.value.find((item) => item.productId === productId);
if (!existing) {
return;
async function decrementQuantity(productId: string) {
await setQuantity(productId, getQuantity(productId) - 1);
}
existing.quantity += 1;
async function removeProduct(productId: string) {
const { data } = await client.mutate({
mutation: RemoveCartItemDocument,
variables: { productId },
fetchPolicy: 'no-cache',
});
if (data?.removeCartItem) {
applyCart(data.removeCartItem);
}
}
function decrementQuantity(productId: string) {
const existing = items.value.find((item) => item.productId === productId);
if (!existing) {
return;
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);
}
}
const normalizedQuantity = normalizeQuantity(existing.quantity - 1);
if (normalizedQuantity === 0) {
removeProduct(productId);
return;
async function clearCart() {
const { data } = await client.mutate({
mutation: ClearCartDocument,
fetchPolicy: 'no-cache',
});
if (data?.clearCart) {
applyCart(data.clearCart);
}
}
existing.quantity = normalizedQuantity;
}
function removeProduct(productId: string) {
items.value = items.value.filter((item) => item.productId !== productId);
}
function clearCart() {
items.value = [];
if (!initialized.value && !loading.value) {
void fetchCart();
}
return {
items,
loading,
selectedDeliveryAddressId,
totalPositions,
totalItems,
totalVolume,
fetchCart,
addProduct,
setQuantity,
incrementQuantity,
decrementQuantity,
removeProduct,
clearCart,
setDeliveryAddress,
getQuantity,
};
}

View File

@@ -16,6 +16,8 @@ const deliveryAddressesQuery = useQuery(MyDeliveryAddressesDocument);
const {
items: cartItems,
fetchCart,
selectedDeliveryAddressId,
totalPositions,
totalItems,
totalVolume,
@@ -23,9 +25,9 @@ const {
decrementQuantity,
removeProduct,
clearCart,
setDeliveryAddress,
} = useClientCart();
const selectedDeliveryAddressId = ref('');
const sending = ref(false);
const success = ref('');
const errorMessage = ref('');
@@ -35,9 +37,11 @@ const hasDeliveryAddresses = computed(() => deliveryAddresses.value.length > 0);
watch(
deliveryAddresses,
(addresses) => {
async (addresses) => {
if (addresses.length < 1) {
selectedDeliveryAddressId.value = '';
if (selectedDeliveryAddressId.value) {
await setDeliveryAddress(null);
}
return;
}
@@ -47,11 +51,15 @@ watch(
}
const defaultAddress = addresses.find((address) => address.isDefault);
selectedDeliveryAddressId.value = defaultAddress?.id || addresses[0]?.id || '';
await setDeliveryAddress(defaultAddress?.id || addresses[0]?.id || null);
},
{ immediate: true },
);
onMounted(() => {
void fetchCart(true);
});
function lineVolume(productId: string) {
const item = cartItems.value.find((entry) => entry.productId === productId);
if (!item) {
@@ -64,19 +72,25 @@ function lineVolume(productId: string) {
function increment(productId: string) {
success.value = '';
errorMessage.value = '';
incrementQuantity(productId);
void incrementQuantity(productId);
}
function decrement(productId: string) {
success.value = '';
errorMessage.value = '';
decrementQuantity(productId);
void decrementQuantity(productId);
}
function removeFromCart(productId: string) {
success.value = '';
errorMessage.value = '';
removeProduct(productId);
void removeProduct(productId);
}
function selectDeliveryAddress(addressId: string) {
success.value = '';
errorMessage.value = '';
void setDeliveryAddress(addressId);
}
async function submitCart() {
@@ -126,7 +140,7 @@ async function submitCart() {
}
sending.value = false;
clearCart();
await clearCart();
success.value = `Отправлено заявок: ${createdCodes.length}. Статус: уточнение цены.`;
}
</script>
@@ -160,11 +174,11 @@ async function submitCart() {
class="flex cursor-pointer items-start gap-3 rounded-2xl bg-white p-3 transition hover:shadow-md"
>
<input
v-model="selectedDeliveryAddressId"
type="radio"
name="delivery-address"
class="radio radio-success mt-1"
:value="address.id"
:checked="selectedDeliveryAddressId === address.id"
@change="selectDeliveryAddress(address.id)"
>
<span>
<span class="block font-semibold text-[#123824]">

View File

@@ -0,0 +1,18 @@
mutation AddProductToCart($productId: ID!) {
addProductToCart(productId: $productId) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}

View File

@@ -0,0 +1,18 @@
mutation ClearCart {
clearCart {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}

View File

@@ -0,0 +1,18 @@
query MyCart {
myCart {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}

View File

@@ -0,0 +1,18 @@
mutation RemoveCartItem($productId: ID!) {
removeCartItem(productId: $productId) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}

View File

@@ -0,0 +1,18 @@
mutation SetCartDeliveryAddress($addressId: ID) {
setCartDeliveryAddress(addressId: $addressId) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}

View File

@@ -0,0 +1,18 @@
mutation UpdateCartItemQuantity($input: UpdateCartItemQuantityInput!) {
updateCartItemQuantity(input: $input) {
id
userId
deliveryAddressId
items {
id
productId
productName
sku
isCustomizable
quantity
parameters
updatedAt
}
updatedAt
}
}

View File

@@ -191,6 +191,28 @@ type Product {
availableInWarehouses: [ProductWarehouseBalance!]!
}
type CartItem {
id: ID!
productId: ID!
productName: String!
sku: String!
isCustomizable: Boolean!
quantity: Float!
parameters: JSON!
createdAt: DateTime!
updatedAt: DateTime!
}
type Cart {
id: ID!
userId: ID!
deliveryAddressId: ID
deliveryAddress: DeliveryAddress
items: [CartItem!]!
createdAt: DateTime!
updatedAt: DateTime!
}
type OrderItem {
id: ID!
productId: ID
@@ -266,6 +288,7 @@ type Query {
healthcheck: String!
me: User
myCounterpartyProfile: CounterpartyProfile
myCart: Cart!
myDeliveryAddresses: [DeliveryAddress!]!
myMessengerConnections: [MessengerConnection!]!
myNotificationHistory(channel: MessengerType!, limit: Int = 50): [NotificationHistoryItem!]!
@@ -340,6 +363,11 @@ input CreateMyDeliveryAddressInput {
fiasId: String
}
input UpdateCartItemQuantityInput {
productId: ID!
quantity: Float!
}
input ReadyOrderItemInput {
productId: ID!
quantity: Float!
@@ -400,6 +428,11 @@ type Mutation {
acceptInvitation(input: AcceptInvitationInput!): User!
connectMessenger(input: ConnectMessengerInput!): MessengerConnection!
upsertMyCounterpartyProfile(input: UpsertMyCounterpartyProfileInput!): CounterpartyProfile!
addProductToCart(productId: ID!): Cart!
updateCartItemQuantity(input: UpdateCartItemQuantityInput!): Cart!
removeCartItem(productId: ID!): Cart!
setCartDeliveryAddress(addressId: ID): Cart!
clearCart: Cart!
createMyDeliveryAddress(input: CreateMyDeliveryAddressInput!): DeliveryAddress!
setMyDefaultDeliveryAddress(addressId: ID!): DeliveryAddress!
deleteMyDeliveryAddress(addressId: ID!): Boolean!