Compare commits
No commits in common. "c36135d78de1895a687bfab38dc0799d1eb945f2" and "af477362d26beb2671456af44eb66dc32fb7f3a6" have entirely different histories.
c36135d78d
...
af477362d2
23 changed files with 411 additions and 1043 deletions
|
|
@ -78,8 +78,6 @@ class CategoryDetailSerializer(ModelSerializer):
|
||||||
"markup_percent",
|
"markup_percent",
|
||||||
"filterable_attributes",
|
"filterable_attributes",
|
||||||
"brands",
|
"brands",
|
||||||
"min_price",
|
|
||||||
"max_price",
|
|
||||||
"children",
|
"children",
|
||||||
"slug",
|
"slug",
|
||||||
"created",
|
"created",
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@
|
||||||
<h6>{{ t('footer.help') }}</h6>
|
<h6>{{ t('footer.help') }}</h6>
|
||||||
<nuxt-link-locale to="/contact">{{ t('contact.title') }}</nuxt-link-locale>
|
<nuxt-link-locale to="/contact">{{ t('contact.title') }}</nuxt-link-locale>
|
||||||
<nuxt-link-locale to="/docs/privacy-policy">{{ t('docs.policy.title') }}</nuxt-link-locale>
|
<nuxt-link-locale to="/docs/privacy-policy">{{ t('docs.policy.title') }}</nuxt-link-locale>
|
||||||
<nuxt-link-locale to="/docs/terms-and-conditions">{{ t('docs.terms.title') }}</nuxt-link-locale>
|
<nuxt-link-locale to="/docs/terms-and-condition">{{ t('docs.terms.title') }}</nuxt-link-locale>
|
||||||
<nuxt-link-locale to="/docs/shipping-information">{{ t('docs.shipping.title') }}</nuxt-link-locale>
|
<nuxt-link-locale to="/docs/shipping-information">{{ t('docs.shipping.title') }}</nuxt-link-locale>
|
||||||
<nuxt-link-locale to="/docs/return-policy">{{ t('docs.return.title') }}</nuxt-link-locale>
|
<nuxt-link-locale to="/docs/refund-policy">{{ t('docs.return.title') }}</nuxt-link-locale>
|
||||||
<nuxt-link-locale to="/docs/about-us">{{ t('docs.about.title') }}</nuxt-link-locale>
|
<nuxt-link-locale to="/docs/about-us">{{ t('docs.about.title') }}</nuxt-link-locale>
|
||||||
<nuxt-link-locale to="/docs/faq">{{ t('docs.faq.title') }}</nuxt-link-locale>
|
<nuxt-link-locale to="/docs/faq">{{ t('docs.faq.title') }}</nuxt-link-locale>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -119,35 +119,22 @@ const appStore = useAppStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const wishlistStore = useWishlistStore();
|
const wishlistStore = useWishlistStore();
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
const { $appHelpers } = useNuxtApp();
|
|
||||||
|
|
||||||
const { uiConfig } = useProjectConfig();
|
const { uiConfig } = useProjectConfig();
|
||||||
|
|
||||||
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
||||||
const user = computed(() => userStore.user);
|
const user = computed(() => userStore.user);
|
||||||
|
|
||||||
const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
|
|
||||||
default: () => [],
|
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
const productsInCartQuantity = computed(() => {
|
const productsInCartQuantity = computed(() => {
|
||||||
if (isAuthenticated.value) {
|
let count = 0;
|
||||||
let count = 0;
|
cartStore.currentOrder?.orderProducts?.edges.forEach((el) => {
|
||||||
cartStore.currentOrder?.orderProducts?.edges.forEach((el) => {
|
count = count + el.node.quantity;
|
||||||
count = count + el.node.quantity;
|
});
|
||||||
});
|
|
||||||
return count;
|
return count;
|
||||||
} else {
|
|
||||||
return cookieCart.value.reduce((acc, item) => acc + item.quantity, 0);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const productsInWishlistQuantity = computed(() => {
|
const productsInWishlistQuantity = computed(() => {
|
||||||
if (isAuthenticated.value) {
|
return wishlistStore.wishlist ? wishlistStore.wishlist.products.edges.length : 0;
|
||||||
return wishlistStore.wishlist ? wishlistStore.wishlist.products.edges.length : 0;
|
|
||||||
} else {
|
|
||||||
return cookieWishlist.value.length
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const isSearchVisible = ref<boolean>(false);
|
const isSearchVisible = ref<boolean>(false);
|
||||||
|
|
@ -315,7 +302,7 @@ const redirectTo = (to) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
&-avatar {
|
&-avatar {
|
||||||
width: 28px;
|
width: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 1px solid #374151;
|
border: 1px solid #374151;
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
|
|
@ -330,7 +317,7 @@ const redirectTo = (to) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
&-profile {
|
&-profile {
|
||||||
width: 28px;
|
width: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border: 1px solid #374151;
|
border: 1px solid #374151;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
import type {IPost} from '@types';
|
import type {IPost} from '@types';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
post: IPost;
|
post: { node: IPost }[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@
|
||||||
class="card__wishlist"
|
class="card__wishlist"
|
||||||
@click="overwriteWishlist({
|
@click="overwriteWishlist({
|
||||||
type: (isProductInWishlist ? 'remove' : 'add'),
|
type: (isProductInWishlist ? 'remove' : 'add'),
|
||||||
product: product,
|
productUuid: product.uuid,
|
||||||
|
productName: product.name
|
||||||
})"
|
})"
|
||||||
>
|
>
|
||||||
<icon style="color: #dc2626;" name="mdi:cards-heart" size="16" v-if="isProductInWishlist" />
|
<icon name="mdi:cards-heart" size="16" v-if="isProductInWishlist" />
|
||||||
<icon name="mdi:cards-heart-outline" size="16" v-else />
|
<icon name="mdi:cards-heart-outline" size="16" v-else />
|
||||||
</div>
|
</div>
|
||||||
<div class="card__wrapper">
|
<div class="card__wrapper">
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,6 @@ export function useLogin() {
|
||||||
userStore.setUser(authData.user);
|
userStore.setUser(authData.user);
|
||||||
cookieAccess.value = authData.accessToken;
|
cookieAccess.value = authData.accessToken;
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
navigateTo(localePath('/'));
|
navigateTo(localePath('/'));
|
||||||
|
|
||||||
useNotification({
|
useNotification({
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,8 @@ export function useBreadcrumbs() {
|
||||||
return crumbs;
|
return crumbs;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(breadcrumbs.value)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
breadcrumbs,
|
breadcrumbs,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
export * from './useOrderBuy';
|
export * from './useOrderBuy';
|
||||||
export * from './useOrderOverwrite';
|
export * from './useOrderOverwrite';
|
||||||
export * from './useOrderSync';
|
|
||||||
export * from './useOrders';
|
export * from './useOrders';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from '@graphql/mutations/cart';
|
} from '@graphql/mutations/cart';
|
||||||
import type {
|
import type {
|
||||||
IAddToOrderResponse,
|
IAddToOrderResponse,
|
||||||
IBulkOrderResponse, IProduct,
|
IBulkOrderResponse,
|
||||||
IRemoveAllFromOrderResponse,
|
IRemoveAllFromOrderResponse,
|
||||||
IRemoveFromOrderResponse,
|
IRemoveFromOrderResponse,
|
||||||
IRemoveKindFromOrderResponse,
|
IRemoveKindFromOrderResponse,
|
||||||
|
|
@ -16,9 +16,9 @@ import type {
|
||||||
|
|
||||||
interface IOverwriteOrderArguments {
|
interface IOverwriteOrderArguments {
|
||||||
type: string;
|
type: string;
|
||||||
product: IProduct;
|
productUuid?: string;
|
||||||
|
productName?: string;
|
||||||
bulkAction?: string;
|
bulkAction?: string;
|
||||||
isBulkSync?: boolean;
|
|
||||||
products?: {
|
products?: {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
@ -32,11 +32,6 @@ export function useOrderOverwrite() {
|
||||||
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
||||||
const orderUuid = computed(() => cartStore.currentOrder?.uuid);
|
const orderUuid = computed(() => cartStore.currentOrder?.uuid);
|
||||||
|
|
||||||
const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, {
|
|
||||||
default: () => [],
|
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutate: addMutate,
|
mutate: addMutate,
|
||||||
loading: addLoading,
|
loading: addLoading,
|
||||||
|
|
@ -69,7 +64,7 @@ export function useOrderOverwrite() {
|
||||||
case 'add': {
|
case 'add': {
|
||||||
const addResult = await addMutate({
|
const addResult = await addMutate({
|
||||||
orderUuid: orderUuid.value,
|
orderUuid: orderUuid.value,
|
||||||
productUuid: args.product.uuid,
|
productUuid: args.productUuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (addResult?.data?.addOrderProduct?.order) {
|
if (addResult?.data?.addOrderProduct?.order) {
|
||||||
|
|
@ -77,7 +72,7 @@ export function useOrderOverwrite() {
|
||||||
|
|
||||||
useNotification({
|
useNotification({
|
||||||
message: t('popup.success.addToCart', {
|
message: t('popup.success.addToCart', {
|
||||||
product: args.product.name,
|
product: args.productName,
|
||||||
}),
|
}),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
@ -89,7 +84,7 @@ export function useOrderOverwrite() {
|
||||||
case 'remove': {
|
case 'remove': {
|
||||||
const removeResult = await removeMutate({
|
const removeResult = await removeMutate({
|
||||||
orderUuid: orderUuid.value,
|
orderUuid: orderUuid.value,
|
||||||
productUuid: args.product.uuid,
|
productUuid: args.productUuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeResult?.data?.removeOrderProduct?.order) {
|
if (removeResult?.data?.removeOrderProduct?.order) {
|
||||||
|
|
@ -97,7 +92,7 @@ export function useOrderOverwrite() {
|
||||||
|
|
||||||
useNotification({
|
useNotification({
|
||||||
message: t('popup.success.removeFromCart', {
|
message: t('popup.success.removeFromCart', {
|
||||||
product: args.product.name,
|
product: args.productName,
|
||||||
}),
|
}),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
@ -109,7 +104,7 @@ export function useOrderOverwrite() {
|
||||||
case 'removeKind': {
|
case 'removeKind': {
|
||||||
const removeKindResult = await removeKindMutate({
|
const removeKindResult = await removeKindMutate({
|
||||||
orderUuid: orderUuid.value,
|
orderUuid: orderUuid.value,
|
||||||
productUuid: args.product.uuid,
|
productUuid: args.productUuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeKindResult?.data?.removeOrderProductsOfAKind?.order) {
|
if (removeKindResult?.data?.removeOrderProductsOfAKind?.order) {
|
||||||
|
|
@ -117,7 +112,7 @@ export function useOrderOverwrite() {
|
||||||
|
|
||||||
useNotification({
|
useNotification({
|
||||||
message: t('popup.success.removeFromCart', {
|
message: t('popup.success.removeFromCart', {
|
||||||
product: args.product.name,
|
product: args.productName,
|
||||||
}),
|
}),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
@ -153,7 +148,7 @@ export function useOrderOverwrite() {
|
||||||
if (bulkResult?.data?.bulkOrderAction?.order) {
|
if (bulkResult?.data?.bulkOrderAction?.order) {
|
||||||
cartStore.setCurrentOrders(bulkResult.data.bulkOrderAction.order);
|
cartStore.setCurrentOrders(bulkResult.data.bulkOrderAction.order);
|
||||||
useNotification({
|
useNotification({
|
||||||
message: t('popup.success.bulkRemoveOrder'),
|
message: t('popup.success.bulkRemoveWishlist'),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -165,119 +160,10 @@ export function useOrderOverwrite() {
|
||||||
console.error('No type provided for overwriteOrder');
|
console.error('No type provided for overwriteOrder');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (args.type) {
|
useNotification({
|
||||||
case 'add': {
|
message: t('popup.errors.loginFirst'),
|
||||||
const currentCart = cookieCart.value || [];
|
type: 'error',
|
||||||
const existingItem = currentCart.find(
|
});
|
||||||
(item) => item.product.uuid === args.product.uuid
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingItem) {
|
|
||||||
existingItem.quantity += 1;
|
|
||||||
cookieCart.value = [...currentCart];
|
|
||||||
} else {
|
|
||||||
cookieCart.value = [
|
|
||||||
...currentCart,
|
|
||||||
{ product: args.product, quantity: 1 }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.addToCart', {
|
|
||||||
product: args.product.name,
|
|
||||||
}),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'remove': {
|
|
||||||
const currentCart = cookieCart.value || [];
|
|
||||||
const existingItem = currentCart.find(
|
|
||||||
(item) => item.product.uuid === args.product.uuid
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingItem) {
|
|
||||||
if (existingItem.quantity > 1) {
|
|
||||||
existingItem.quantity -= 1;
|
|
||||||
cookieCart.value = [...currentCart];
|
|
||||||
} else {
|
|
||||||
cookieCart.value = currentCart.filter(
|
|
||||||
(item) => item.product.uuid !== args.product.uuid
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.removeFromCart', {
|
|
||||||
product: args.product.name,
|
|
||||||
}),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'removeKind': {
|
|
||||||
cookieCart.value = cookieCart.value.filter(
|
|
||||||
(item) => item.product.uuid !== args.product.uuid
|
|
||||||
);
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.removeFromCart', {
|
|
||||||
product: args.product.name,
|
|
||||||
}),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'removeAll': {
|
|
||||||
cookieCart.value = [];
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.removeAllFromCart'),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'bulk': {
|
|
||||||
if (args.bulkAction === 'remove' && args.products) {
|
|
||||||
const uuidsToRemove = args.products.map(p => p.uuid);
|
|
||||||
cookieCart.value = cookieCart.value.filter(
|
|
||||||
(item) => !uuidsToRemove.includes(item.product.uuid)
|
|
||||||
);
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.bulkRemoveOrder'),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
} else if (args.bulkAction === 'add' && args.products) {
|
|
||||||
const currentCart = cookieCart.value || [];
|
|
||||||
|
|
||||||
for (const productRef of args.products) {
|
|
||||||
const existingItem = currentCart.find(
|
|
||||||
(item) => item.product.uuid === productRef.uuid
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingItem) {
|
|
||||||
existingItem.quantity += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cookieCart.value = [...currentCart];
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.error('No type provided for overwriteOrder');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
import { useOrderOverwrite } from '@composables/orders/useOrderOverwrite';
|
|
||||||
|
|
||||||
export function useOrderSync() {
|
|
||||||
const cartStore = useCartStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const { $appHelpers } = useNuxtApp();
|
|
||||||
|
|
||||||
const { overwriteOrder } = useOrderOverwrite();
|
|
||||||
|
|
||||||
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
|
||||||
const orderUuid = computed(() => cartStore.currentOrder?.uuid);
|
|
||||||
|
|
||||||
const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, {
|
|
||||||
default: () => [],
|
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
async function syncOrder() {
|
|
||||||
if (!isAuthenticated.value || !orderUuid.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cookieCartItems = cookieCart.value || [];
|
|
||||||
|
|
||||||
if (cookieCartItems.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiCartProducts = cartStore.currentOrder?.orderProducts?.edges || [];
|
|
||||||
|
|
||||||
const apiProductMap = new Map(
|
|
||||||
apiCartProducts.map(e => [e.node.product.uuid, e.node.quantity])
|
|
||||||
);
|
|
||||||
|
|
||||||
const productsToSync = [];
|
|
||||||
|
|
||||||
for (const cartItem of cookieCartItems) {
|
|
||||||
const apiQuantity = apiProductMap.get(cartItem.product.uuid) || 0;
|
|
||||||
const quantityDifference = cartItem.quantity - apiQuantity;
|
|
||||||
|
|
||||||
if (quantityDifference > 0) {
|
|
||||||
for (let i = 0; i < quantityDifference; i++) {
|
|
||||||
productsToSync.push({ uuid: cartItem.product.uuid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (productsToSync.length === 0) {
|
|
||||||
cookieCart.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await overwriteOrder({
|
|
||||||
type: 'bulk',
|
|
||||||
bulkAction: 'add',
|
|
||||||
isBulkSync: true,
|
|
||||||
products: productsToSync
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to sync cart:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
syncOrder,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { orderStatuses } from '@appConstants';
|
import { orderStatuses } from '@appConstants';
|
||||||
import { useOrderSync } from '@composables/orders';
|
|
||||||
import {useWishlistSync} from "@composables/wishlist";
|
|
||||||
import { getUserBaseData } from '@graphql/queries/combined/userBaseData';
|
import { getUserBaseData } from '@graphql/queries/combined/userBaseData';
|
||||||
import type { IUserBaseDataResponse } from '@types';
|
import type { IUserBaseDataResponse } from '@types';
|
||||||
|
|
||||||
|
|
@ -9,9 +7,6 @@ export async function useUserBaseData(userEmail: string) {
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
const promocodeStore = usePromocodeStore();
|
const promocodeStore = usePromocodeStore();
|
||||||
|
|
||||||
const { syncWishlist } = useWishlistSync();
|
|
||||||
const { syncOrder } = useOrderSync();
|
|
||||||
|
|
||||||
const { document, variables } = getUserBaseData({
|
const { document, variables } = getUserBaseData({
|
||||||
userEmail,
|
userEmail,
|
||||||
status: orderStatuses.PENDING,
|
status: orderStatuses.PENDING,
|
||||||
|
|
@ -24,13 +19,9 @@ export async function useUserBaseData(userEmail: string) {
|
||||||
|
|
||||||
if (data?.wishlists.edges) {
|
if (data?.wishlists.edges) {
|
||||||
wishlistStore.setWishlist(data.wishlists.edges[0].node);
|
wishlistStore.setWishlist(data.wishlists.edges[0].node);
|
||||||
|
|
||||||
await syncWishlist();
|
|
||||||
}
|
}
|
||||||
if (data?.orders.edges) {
|
if (data?.orders.edges) {
|
||||||
cartStore.setCurrentOrders(data.orders.edges[0].node);
|
cartStore.setCurrentOrders(data.orders.edges[0].node);
|
||||||
|
|
||||||
await syncOrder();
|
|
||||||
}
|
}
|
||||||
if (data?.promocodes.edges) {
|
if (data?.promocodes.edges) {
|
||||||
promocodeStore.setPromocodes(data.promocodes.edges);
|
promocodeStore.setPromocodes(data.promocodes.edges);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
export * from './useWishlist';
|
export * from './useWishlist';
|
||||||
export * from './useWishlistOverwrite';
|
export * from './useWishlistOverwrite';
|
||||||
export * from './useWishlistSync';
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,16 @@ import {
|
||||||
} from '@graphql/mutations/wishlist';
|
} from '@graphql/mutations/wishlist';
|
||||||
import type {
|
import type {
|
||||||
IAddToWishlistResponse,
|
IAddToWishlistResponse,
|
||||||
IBulkWishlistResponse, IProduct,
|
IBulkWishlistResponse,
|
||||||
IRemoveAllFromWishlistResponse,
|
IRemoveAllFromWishlistResponse,
|
||||||
IRemoveFromWishlistResponse,
|
IRemoveFromWishlistResponse,
|
||||||
} from '@types';
|
} from '@types';
|
||||||
|
|
||||||
interface IOverwriteWishlistArguments {
|
interface IOverwriteWishlistArguments {
|
||||||
type: string;
|
type: string;
|
||||||
product: IProduct;
|
productUuid?: string;
|
||||||
|
productName?: string;
|
||||||
bulkAction?: string;
|
bulkAction?: string;
|
||||||
isBulkSync?: boolean;
|
|
||||||
products?: {
|
products?: {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
@ -26,16 +26,10 @@ export function useWishlistOverwrite() {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const wishlistStore = useWishlistStore();
|
const wishlistStore = useWishlistStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { $appHelpers } = useNuxtApp();
|
|
||||||
|
|
||||||
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
||||||
const wishlistUuid = computed(() => wishlistStore.wishlist?.uuid);
|
const wishlistUuid = computed(() => wishlistStore.wishlist?.uuid);
|
||||||
|
|
||||||
const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
|
|
||||||
default: () => [],
|
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutate: addMutate,
|
mutate: addMutate,
|
||||||
loading: addLoading,
|
loading: addLoading,
|
||||||
|
|
@ -63,7 +57,7 @@ export function useWishlistOverwrite() {
|
||||||
case 'add': {
|
case 'add': {
|
||||||
const addResult = await addMutate({
|
const addResult = await addMutate({
|
||||||
wishlistUuid: wishlistUuid.value,
|
wishlistUuid: wishlistUuid.value,
|
||||||
productUuid: args.product.uuid,
|
productUuid: args.productUuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (addResult?.data?.addWishlistProduct?.wishlist) {
|
if (addResult?.data?.addWishlistProduct?.wishlist) {
|
||||||
|
|
@ -71,7 +65,7 @@ export function useWishlistOverwrite() {
|
||||||
|
|
||||||
useNotification({
|
useNotification({
|
||||||
message: t('popup.success.addToWishlist', {
|
message: t('popup.success.addToWishlist', {
|
||||||
product: args.product.name,
|
product: args.productName,
|
||||||
}),
|
}),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
@ -83,7 +77,7 @@ export function useWishlistOverwrite() {
|
||||||
case 'remove': {
|
case 'remove': {
|
||||||
const removeResult = await removeMutate({
|
const removeResult = await removeMutate({
|
||||||
wishlistUuid: wishlistUuid.value,
|
wishlistUuid: wishlistUuid.value,
|
||||||
productUuid: args.product.uuid,
|
productUuid: args.productUuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeResult?.data?.removeWishlistProduct?.wishlist) {
|
if (removeResult?.data?.removeWishlistProduct?.wishlist) {
|
||||||
|
|
@ -91,7 +85,7 @@ export function useWishlistOverwrite() {
|
||||||
|
|
||||||
useNotification({
|
useNotification({
|
||||||
message: t('popup.success.removeFromWishlist', {
|
message: t('popup.success.removeFromWishlist', {
|
||||||
product: args.product.name,
|
product: args.productName,
|
||||||
}),
|
}),
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
|
@ -103,7 +97,7 @@ export function useWishlistOverwrite() {
|
||||||
case 'removeAll': {
|
case 'removeAll': {
|
||||||
const removeAllResult = await removeAllMutate({
|
const removeAllResult = await removeAllMutate({
|
||||||
wishlistUuid: wishlistUuid.value,
|
wishlistUuid: wishlistUuid.value,
|
||||||
productUuid: args.product.uuid,
|
productUuid: args.productUuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeAllResult?.data?.removeAllWishlistProducts?.wishlist) {
|
if (removeAllResult?.data?.removeAllWishlistProducts?.wishlist) {
|
||||||
|
|
@ -127,15 +121,10 @@ export function useWishlistOverwrite() {
|
||||||
|
|
||||||
if (bulkResult?.data?.bulkWishlistAction?.wishlist) {
|
if (bulkResult?.data?.bulkWishlistAction?.wishlist) {
|
||||||
wishlistStore.setWishlist(bulkResult.data.bulkWishlistAction.wishlist);
|
wishlistStore.setWishlist(bulkResult.data.bulkWishlistAction.wishlist);
|
||||||
|
useNotification({
|
||||||
if (args.isBulkSync) {
|
message: t('popup.success.bulkRemoveWishlist'),
|
||||||
cookieWishlist.value = [];
|
type: 'success',
|
||||||
} else {
|
});
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.bulkRemoveWishlist'),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -145,85 +134,10 @@ export function useWishlistOverwrite() {
|
||||||
console.error('No type provided for overwriteWishlist');
|
console.error('No type provided for overwriteWishlist');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (args.type) {
|
useNotification({
|
||||||
case 'add': {
|
message: t('popup.errors.loginFirst'),
|
||||||
const isAlreadyInWishlist = cookieWishlist.value.some(
|
type: 'error',
|
||||||
(item) => item.uuid === args.product.uuid
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (isAlreadyInWishlist) {
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.errors.alreadyInWishlist', {
|
|
||||||
product: args.product.name,
|
|
||||||
}),
|
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
cookieWishlist.value = [...cookieWishlist.value, args.product];
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.addToWishlist', {
|
|
||||||
product: args.product.name,
|
|
||||||
}),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'remove': {
|
|
||||||
cookieWishlist.value = cookieWishlist.value.filter(
|
|
||||||
(item) => item.uuid !== args.product.uuid
|
|
||||||
);
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.removeFromWishlist', {
|
|
||||||
product: args.product.name,
|
|
||||||
}),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'removeAll': {
|
|
||||||
cookieWishlist.value = [];
|
|
||||||
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.removeAllFromWishlist'),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'bulk': {
|
|
||||||
const bulkResult = await bulkMutate({
|
|
||||||
wishlistUuid: wishlistUuid.value,
|
|
||||||
action: args.bulkAction,
|
|
||||||
products: args.products,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (bulkResult?.data?.bulkWishlistAction?.wishlist) {
|
|
||||||
wishlistStore.setWishlist(bulkResult.data.bulkWishlistAction.wishlist);
|
|
||||||
|
|
||||||
if (args.isBulkSync) {
|
|
||||||
cookieWishlist.value = [];
|
|
||||||
} else {
|
|
||||||
useNotification({
|
|
||||||
message: t('popup.success.bulkRemoveWishlist'),
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.error('No type provided for overwriteWishlist');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import {useWishlistOverwrite} from "@composables/wishlist/useWishlistOverwrite";
|
|
||||||
|
|
||||||
export function useWishlistSync() {
|
|
||||||
const wishlistStore = useWishlistStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const { $appHelpers } = useNuxtApp();
|
|
||||||
|
|
||||||
const { overwriteWishlist } = useWishlistOverwrite();
|
|
||||||
|
|
||||||
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
|
||||||
const wishlistUuid = computed(() => wishlistStore.wishlist?.uuid);
|
|
||||||
|
|
||||||
const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
|
|
||||||
default: () => [],
|
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
async function syncWishlist() {
|
|
||||||
if (!isAuthenticated.value || !wishlistUuid.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cookieProducts = cookieWishlist.value || [];
|
|
||||||
|
|
||||||
if (cookieProducts.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiProductUuids = wishlistStore.wishlist?.products?.edges.map(e => e.node.uuid) || [];
|
|
||||||
|
|
||||||
const productsToAdd = cookieProducts.filter(
|
|
||||||
(product) => !apiProductUuids.includes(product.uuid)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (productsToAdd.length === 0) {
|
|
||||||
cookieWishlist.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await overwriteWishlist({
|
|
||||||
type: 'bulk',
|
|
||||||
bulkAction: 'add',
|
|
||||||
isBulkSync: true,
|
|
||||||
products: productsToAdd.map(p => ({ uuid: p.uuid }))
|
|
||||||
})
|
|
||||||
|
|
||||||
if (bulkResult?.data?.bulkWishlistAction?.wishlist) {
|
|
||||||
wishlistStore.setWishlist(bulkResult.data.bulkWishlistAction.wishlist);
|
|
||||||
|
|
||||||
cookieWishlist.value = [];
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to sync wishlist:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(syncError, (err) => {
|
|
||||||
if (!err) return;
|
|
||||||
console.error('useWishlistSync error:', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
syncWishlist,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -2,8 +2,6 @@ export const COOKIE_KEY_TEMPLATES = {
|
||||||
LOCALE: (appNameKey: string) => `${appNameKey}-locale`,
|
LOCALE: (appNameKey: string) => `${appNameKey}-locale`,
|
||||||
REFRESH_TOKEN: (appNameKey: string) => `${appNameKey}-refresh`,
|
REFRESH_TOKEN: (appNameKey: string) => `${appNameKey}-refresh`,
|
||||||
ACCESS_TOKEN: (appNameKey: string) => `${appNameKey}-access`,
|
ACCESS_TOKEN: (appNameKey: string) => `${appNameKey}-access`,
|
||||||
WISHLIST_TOKEN: (appNameKey: string) => `${appNameKey}-wishlist`,
|
|
||||||
CART_TOKEN: (appNameKey: string) => `${appNameKey}-cart`,
|
|
||||||
PRODUCT_VIEW: (appNameKey: string) => `${appNameKey}-product-view`,
|
PRODUCT_VIEW: (appNameKey: string) => `${appNameKey}-product-view`,
|
||||||
THEME: (appNameKey: string) => `${appNameKey}-theme`,
|
THEME: (appNameKey: string) => `${appNameKey}-theme`,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
||||||
|
|
@ -4,27 +4,7 @@
|
||||||
<div class="cart__wrapper">
|
<div class="cart__wrapper">
|
||||||
<div class="cart__top">
|
<div class="cart__top">
|
||||||
<h1 class="cart__top-title">{{ t('cart.title') }}</h1>
|
<h1 class="cart__top-title">{{ t('cart.title') }}</h1>
|
||||||
<div class="cart__top-inner">
|
<p>({{ t('cart.items', productsInCartQuantity, { count: productsInCartQuantity }) }})</p>
|
||||||
<p>({{ t('cart.items', productsInCartQuantity, { count: productsInCartQuantity }) }})</p>
|
|
||||||
<ui-button
|
|
||||||
:type="'button'"
|
|
||||||
class="cart__top-button"
|
|
||||||
@click="buyOrder"
|
|
||||||
>
|
|
||||||
<icon name="material-symbols:add" size="20" />
|
|
||||||
{{ t('buttons.checkout') }}
|
|
||||||
</ui-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="cart__list">
|
|
||||||
<div class="cart__list-inner" v-if="productsInCart.length">
|
|
||||||
<cards-product
|
|
||||||
v-for="product in productsInCart"
|
|
||||||
:key="product.node.uuid"
|
|
||||||
:product="product.node.product"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p class="cart__empty" v-else>{{ t('cart.empty') }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -33,55 +13,23 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {usePageTitle} from "@composables/utils";
|
import {usePageTitle} from "@composables/utils";
|
||||||
import {useOrderBuy} from "~/composables/orders";
|
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
const userStore = useUserStore();
|
|
||||||
const { $appHelpers } = useNuxtApp();
|
|
||||||
|
|
||||||
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
|
||||||
|
|
||||||
const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, {
|
|
||||||
default: () => [],
|
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
const { buyOrder } = useOrderBuy();
|
|
||||||
|
|
||||||
const productsInCart = computed(() => {
|
const productsInCart = computed(() => {
|
||||||
if (isAuthenticated.value) {
|
return cartStore.currentOrder ? cartStore.currentOrder.orderProducts.edges : [];
|
||||||
return cartStore.currentOrder ? cartStore.currentOrder.orderProducts.edges : [];
|
|
||||||
} else {
|
|
||||||
return cookieCart.value.map(product => ({
|
|
||||||
node: {
|
|
||||||
product: item.product,
|
|
||||||
quantity: item.quantity
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPrice = computed(() => {
|
const totalPrice = computed(() => {
|
||||||
if (isAuthenticated.value) {
|
return cartStore.currentOrder ? cartStore.currentOrder.totalPrice : 0;
|
||||||
return cartStore.currentOrder ? cartStore.currentOrder.totalPrice : 0;
|
|
||||||
} else {
|
|
||||||
return cookieCart.value.reduce((acc, item) => {
|
|
||||||
return acc + (item.product.price * item.quantity);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const productsInCartQuantity = computed(() => {
|
const productsInCartQuantity = computed(() => {
|
||||||
if (isAuthenticated.value) {
|
let count = 0;
|
||||||
let count = 0;
|
cartStore.currentOrder?.orderProducts?.edges.forEach((el) => {
|
||||||
cartStore.currentOrder?.orderProducts?.edges.forEach((el) => {
|
count = count + el.node.quantity;
|
||||||
count = count + el.node.quantity;
|
});
|
||||||
});
|
|
||||||
return count;
|
return count;
|
||||||
} else {
|
|
||||||
return cookieCart.value.reduce((acc, item) => acc + item.quantity, 0);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { setPageTitle } = usePageTitle();
|
const { setPageTitle } = usePageTitle();
|
||||||
|
|
@ -97,7 +45,7 @@ setPageTitle(t('breadcrumbs.cart'));
|
||||||
&__wrapper {
|
&__wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 50px;
|
gap: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__top {
|
&__top {
|
||||||
|
|
@ -105,12 +53,6 @@ setPageTitle(t('breadcrumbs.cart'));
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
&-inner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-title {
|
&-title {
|
||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
font-family: "Playfair Display", sans-serif;
|
font-family: "Playfair Display", sans-serif;
|
||||||
|
|
@ -125,19 +67,6 @@ setPageTitle(t('breadcrumbs.cart'));
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
letter-spacing: -0.5px;
|
letter-spacing: -0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-button {
|
|
||||||
width: fit-content;
|
|
||||||
padding-inline: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__list {
|
|
||||||
&-inner {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
||||||
gap: 25px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -79,7 +79,8 @@
|
||||||
class="product__main-button"
|
class="product__main-button"
|
||||||
@click="overwriteWishlist({
|
@click="overwriteWishlist({
|
||||||
type: (isProductInWishlist ? 'remove' : 'add'),
|
type: (isProductInWishlist ? 'remove' : 'add'),
|
||||||
product: product
|
productUuid: product.uuid,
|
||||||
|
productName: product.name
|
||||||
})"
|
})"
|
||||||
:type="'button'"
|
:type="'button'"
|
||||||
:style="'secondary'"
|
:style="'secondary'"
|
||||||
|
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="wishlist">
|
|
||||||
<div class="container">
|
|
||||||
<div class="wishlist__wrapper">
|
|
||||||
<div class="wishlist__top">
|
|
||||||
<h1 class="wishlist__top-title">{{ t('wishlist.title') }}</h1>
|
|
||||||
<div class="wishlist__top-inner">
|
|
||||||
<p>({{ t('wishlist.items', productsInWishlist.length, { count: productsInWishlist.length }) }})</p>
|
|
||||||
<ui-button
|
|
||||||
:type="'button'"
|
|
||||||
class="wishlist__top-button"
|
|
||||||
@click="onBulkAdd"
|
|
||||||
:isLoading="bulkLoading"
|
|
||||||
>
|
|
||||||
<icon name="material-symbols:add" size="20" />
|
|
||||||
{{ t('buttons.addAllToCart') }}
|
|
||||||
</ui-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="wishlist__list">
|
|
||||||
<div class="wishlist__list-inner" v-if="productsInWishlist.length">
|
|
||||||
<cards-product
|
|
||||||
v-for="product in productsInWishlist"
|
|
||||||
:key="product.node.uuid"
|
|
||||||
:product="product.node"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p class="wishlist__empty" v-else>{{ t('wishlist.empty') }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import {usePageTitle} from '@composables/utils';
|
|
||||||
import {useWishlistOverwrite} from '@composables/wishlist';
|
|
||||||
import {useOrderOverwrite} from "@composables/orders";
|
|
||||||
|
|
||||||
const {t} = useI18n();
|
|
||||||
const wishlistStore = useWishlistStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const { $appHelpers } = useNuxtApp();
|
|
||||||
|
|
||||||
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
|
||||||
|
|
||||||
const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
|
|
||||||
default: () => [],
|
|
||||||
path: '/',
|
|
||||||
});
|
|
||||||
|
|
||||||
const { overwriteWishlist } = useWishlistOverwrite();
|
|
||||||
const { overwriteOrder, bulkLoading } = useOrderOverwrite();
|
|
||||||
|
|
||||||
const productsInWishlist = computed(() => {
|
|
||||||
if (isAuthenticated.value) {
|
|
||||||
return wishlistStore.wishlist ? wishlistStore.wishlist.products.edges : [];
|
|
||||||
} else {
|
|
||||||
return cookieWishlist.value.map(product => ({
|
|
||||||
node: product
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const totalPrice = computed(() => {
|
|
||||||
return productsInWishlist.value.reduce((acc, p) => acc + p.node.price, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectedProducts = ref<{ uuid: string }[]>([]);
|
|
||||||
|
|
||||||
const productsUuid = computed<boolean>(() => {
|
|
||||||
return productsInWishlist.value.map(p => ({ uuid: p.node.uuid }));
|
|
||||||
});
|
|
||||||
|
|
||||||
function toggleUuid(uuid: string, checked: boolean) {
|
|
||||||
if (checked) {
|
|
||||||
if (!selectedProducts.value.some(o => o.uuid === uuid)) {
|
|
||||||
selectedProducts.value.push({ uuid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
selectedProducts.value = selectedProducts.value
|
|
||||||
.filter(o => o.uuid !== uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onBulkAdd() {
|
|
||||||
overwriteOrder({
|
|
||||||
type: 'bulk',
|
|
||||||
bulkAction: 'add',
|
|
||||||
products: productsUuid.value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onBulkRemove() {
|
|
||||||
overwriteWishlist({
|
|
||||||
type: 'bulkRemove',
|
|
||||||
bulkAction: 'remove',
|
|
||||||
products: productsUuid.value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { setPageTitle } = usePageTitle();
|
|
||||||
|
|
||||||
setPageTitle(t('breadcrumbs.wishlist'));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.wishlist {
|
|
||||||
padding-block: 50px 100px;
|
|
||||||
background-color: $white;
|
|
||||||
|
|
||||||
&__wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__top {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
&-title {
|
|
||||||
color: #1a1a1a;
|
|
||||||
font-family: "Playfair Display", sans-serif;
|
|
||||||
font-size: 36px;
|
|
||||||
font-weight: 600;
|
|
||||||
letter-spacing: -0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-inner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& p {
|
|
||||||
color: #4b5563;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 400;
|
|
||||||
letter-spacing: -0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-button {
|
|
||||||
width: fit-content;
|
|
||||||
padding-inline: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__list {
|
|
||||||
&-inner {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
|
||||||
gap: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
@ -14,8 +14,6 @@ export default defineNuxtPlugin(() => {
|
||||||
COOKIES_LOCALE_KEY: COOKIE_KEY_TEMPLATES.LOCALE(APP_NAME_KEY),
|
COOKIES_LOCALE_KEY: COOKIE_KEY_TEMPLATES.LOCALE(APP_NAME_KEY),
|
||||||
COOKIES_REFRESH_TOKEN_KEY: COOKIE_KEY_TEMPLATES.REFRESH_TOKEN(APP_NAME_KEY),
|
COOKIES_REFRESH_TOKEN_KEY: COOKIE_KEY_TEMPLATES.REFRESH_TOKEN(APP_NAME_KEY),
|
||||||
COOKIES_ACCESS_TOKEN_KEY: COOKIE_KEY_TEMPLATES.ACCESS_TOKEN(APP_NAME_KEY),
|
COOKIES_ACCESS_TOKEN_KEY: COOKIE_KEY_TEMPLATES.ACCESS_TOKEN(APP_NAME_KEY),
|
||||||
COOKIES_WISHLIST_KEY: COOKIE_KEY_TEMPLATES.WISHLIST_TOKEN(APP_NAME_KEY),
|
|
||||||
COOKIES_CART_KEY: COOKIE_KEY_TEMPLATES.CART_TOKEN(APP_NAME_KEY),
|
|
||||||
COOKIES_PRODUCT_VIEW_KEY: COOKIE_KEY_TEMPLATES.PRODUCT_VIEW(APP_NAME_KEY),
|
COOKIES_PRODUCT_VIEW_KEY: COOKIE_KEY_TEMPLATES.PRODUCT_VIEW(APP_NAME_KEY),
|
||||||
COOKIES_THEME_KEY: COOKIE_KEY_TEMPLATES.THEME(APP_NAME_KEY),
|
COOKIES_THEME_KEY: COOKIE_KEY_TEMPLATES.THEME(APP_NAME_KEY),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import { ApolloLink, from } from '@apollo/client/core';
|
import { ApolloLink, from } from '@apollo/client/core';
|
||||||
import { setContext } from '@apollo/client/link/context';
|
import { setContext } from '@apollo/client/link/context';
|
||||||
import { onError } from '@apollo/client/link/error';
|
import { onError } from '@apollo/client/link/error';
|
||||||
|
|
@ -13,47 +12,25 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||||
nuxtApp.callHook('apollo:error', err);
|
nuxtApp.callHook('apollo:error', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const localeCookie = useCookie($appHelpers.COOKIES_LOCALE_KEY);
|
||||||
|
const accessTokenCookie = useCookie($appHelpers.COOKIES_ACCESS_TOKEN_KEY);
|
||||||
|
|
||||||
nuxtApp.hook('apollo:error', (error) => {
|
nuxtApp.hook('apollo:error', (error) => {
|
||||||
console.error('[Apollo Error]:', error);
|
console.error('[Apollo Error]:', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
const authLink = setContext((_, { headers }) => {
|
const authLink = setContext((_, { headers }) => {
|
||||||
let accessToken = '';
|
const acceptLanguage = localeCookie.value || 'en-gb';
|
||||||
let locale = 'en-gb';
|
|
||||||
|
|
||||||
if (import.meta.client) {
|
return {
|
||||||
const clientCookies = document.cookie.split(';').reduce((acc, cookie) => {
|
headers: {
|
||||||
const [key, value] = cookie.trim().split('=');
|
...headers,
|
||||||
acc[key] = decodeURIComponent(value);
|
'Accept-Language': acceptLanguage,
|
||||||
return acc;
|
...(accessTokenCookie.value && {
|
||||||
}, {} as Record<string, string>);
|
'X-SCHON-AUTH': `Bearer ${accessTokenCookie.value}`,
|
||||||
|
}),
|
||||||
accessToken = clientCookies[$appHelpers.COOKIES_ACCESS_TOKEN_KEY] || '';
|
},
|
||||||
locale = clientCookies[$appHelpers.COOKIES_LOCALE_KEY] || 'en-gb';
|
|
||||||
} else {
|
|
||||||
const cookieHeader = nuxtApp.ssrContext?.event?.node?.req?.headers?.cookie || '';
|
|
||||||
const serverCookies = cookieHeader.split(';').reduce((acc, cookie) => {
|
|
||||||
const [key, value] = cookie.trim().split('=');
|
|
||||||
if (key && value) {
|
|
||||||
acc[key] = decodeURIComponent(value);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, string>);
|
|
||||||
|
|
||||||
accessToken = serverCookies[$appHelpers.COOKIES_ACCESS_TOKEN_KEY] || '';
|
|
||||||
locale = serverCookies[$appHelpers.COOKIES_LOCALE_KEY] || 'en-gb';
|
|
||||||
}
|
|
||||||
|
|
||||||
const hdrs: Record<string, string> = {
|
|
||||||
...headers,
|
|
||||||
'Accept-Language': locale
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (accessToken) {
|
|
||||||
hdrs['X-SCHON-AUTH'] = `Bearer ${accessToken}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { headers: hdrs };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const customLink = new ApolloLink((operation, forward) => {
|
const customLink = new ApolloLink((operation, forward) => {
|
||||||
|
|
@ -74,4 +51,4 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||||
]));
|
]));
|
||||||
|
|
||||||
provideApolloClient($apollo.defaultClient);
|
provideApolloClient($apollo.defaultClient);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
"createAccount": "Create Account",
|
"createAccount": "Create Account",
|
||||||
"addToCart": "Add to cart",
|
"addToCart": "Add to cart",
|
||||||
"removeFromCart": "Remove from cart",
|
"removeFromCart": "Remove from cart",
|
||||||
"addAllToCart": "Add all to cart",
|
|
||||||
"addToWishlist": "Add to Wishlist",
|
"addToWishlist": "Add to Wishlist",
|
||||||
"removeFromWishlist": "Remove from Wishlist",
|
"removeFromWishlist": "Remove from Wishlist",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
|
|
@ -60,8 +59,7 @@
|
||||||
"main": "Error!",
|
"main": "Error!",
|
||||||
"defaultError": "Something went wrong..",
|
"defaultError": "Something went wrong..",
|
||||||
"noDataToUpdate": "There is no data to update.",
|
"noDataToUpdate": "There is no data to update.",
|
||||||
"loginFirst": "You should be logged in to do this action!",
|
"loginFirst": "You should be logged in to do this action!"
|
||||||
"alreadyInWishlist": "{product} is already in wishlist!"
|
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"login": "Sign in successes",
|
"login": "Sign in successes",
|
||||||
|
|
@ -77,7 +75,6 @@
|
||||||
"removeFromWishlist": "{product} has been removed from the wishlist!",
|
"removeFromWishlist": "{product} has been removed from the wishlist!",
|
||||||
"removeAllFromWishlist": "You have successfully emptied the wishlist!",
|
"removeAllFromWishlist": "You have successfully emptied the wishlist!",
|
||||||
"bulkRemoveWishlist": "Selected items have been successfully removed from the wishlist!",
|
"bulkRemoveWishlist": "Selected items have been successfully removed from the wishlist!",
|
||||||
"bulkRemoveOrder": "Selected items have been successfully removed from the cart!",
|
|
||||||
"avatarUpload": "You have successfully uploaded an avatar!",
|
"avatarUpload": "You have successfully uploaded an avatar!",
|
||||||
"userUpdate": "Profile successfully updated!",
|
"userUpdate": "Profile successfully updated!",
|
||||||
"emailUpdate": "Check your inbox for a confirmation link to complete your email update.",
|
"emailUpdate": "Check your inbox for a confirmation link to complete your email update.",
|
||||||
|
|
@ -328,14 +325,8 @@
|
||||||
"text": "Discover the latest trends, style inspiration, and fashion insights from our editorial team."
|
"text": "Discover the latest trends, style inspiration, and fashion insights from our editorial team."
|
||||||
},
|
},
|
||||||
"cart": {
|
"cart": {
|
||||||
"title": "My Cart",
|
"title": "Your Cart",
|
||||||
"items": "no items | {count} item | {count} items",
|
"items": "no items | {count} item | {count} items"
|
||||||
"empty": "Your cart is empty."
|
|
||||||
},
|
|
||||||
"wishlist": {
|
|
||||||
"title": "My Wishlist",
|
|
||||||
"items": "no items | {count} item | {count} items",
|
|
||||||
"empty": "Your wishlist is empty."
|
|
||||||
},
|
},
|
||||||
"shop": {
|
"shop": {
|
||||||
"title": "Shop",
|
"title": "Shop",
|
||||||
|
|
|
||||||
|
|
@ -1 +1,335 @@
|
||||||
{}
|
{
|
||||||
|
"buttons": {
|
||||||
|
"login": "Войти",
|
||||||
|
"register": "Регистрация",
|
||||||
|
"createAccount": "Создать аккаунт",
|
||||||
|
"addToCart": "В корзину",
|
||||||
|
"removeFromCart": "Удалить из корзины",
|
||||||
|
"addToWishlist": "В избранное",
|
||||||
|
"removeFromWishlist": "Удалить из избранного",
|
||||||
|
"send": "Отправить",
|
||||||
|
"goEmail": "Перейти к почте",
|
||||||
|
"logout": "Выйти",
|
||||||
|
"checkout": "Оформить заказ",
|
||||||
|
"save": "Сохранить",
|
||||||
|
"sendLink": "Отправить ссылку для сброса",
|
||||||
|
"topUp": "Пополнить",
|
||||||
|
"shopNow": "КУПИТЬ СЕЙЧАС",
|
||||||
|
"shopTheSale": "Купить по акции",
|
||||||
|
"readMore": "Читать далее",
|
||||||
|
"sendMessage": "Отправить сообщение",
|
||||||
|
"saveChanges": "Сохранить изменения",
|
||||||
|
"clearAll": "Очистить всё"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"required": "Это поле обязательно!",
|
||||||
|
"mail": "Введите корректный email!",
|
||||||
|
"compare": "Пароли не совпадают!",
|
||||||
|
"needLower": "Добавьте строчную букву.",
|
||||||
|
"needUpper": "Добавьте заглавную букву.",
|
||||||
|
"needNumber": "Добавьте цифру.",
|
||||||
|
"needMin": "Мин. 8 символов",
|
||||||
|
"needSpecial": "Добавьте спецсимвол: #.?!$%^&*'()_+=:;\"'/>.<,|\\-",
|
||||||
|
"pageNotFound": "Страница не найдена"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"search": "Поиск",
|
||||||
|
"searchOrder": "Поиск заказа",
|
||||||
|
"name": "Имя",
|
||||||
|
"firstName": "Имя",
|
||||||
|
"lastName": "Фамилия",
|
||||||
|
"phoneNumber": "Номер телефона",
|
||||||
|
"email": "Email",
|
||||||
|
"subject": "Тема",
|
||||||
|
"message": "Ваше сообщение",
|
||||||
|
"password": "Пароль",
|
||||||
|
"newPassword": "Новый пароль",
|
||||||
|
"confirmPassword": "Подтвердите пароль",
|
||||||
|
"confirmNewPassword": "Подтвердите новый пароль",
|
||||||
|
"brandsSearch": "Поиск брендов по названию..."
|
||||||
|
},
|
||||||
|
"checkboxes": {
|
||||||
|
"remember": "Запомнить меня",
|
||||||
|
"chooseAll": "Выбрать все",
|
||||||
|
"agree": "Я согласен с {terms} и {policy}",
|
||||||
|
"subscribe": "Подписаться на рассылку новостей об эксклюзивных предложениях и обновлениях"
|
||||||
|
},
|
||||||
|
"popup": {
|
||||||
|
"errors": {
|
||||||
|
"main": "Ошибка!",
|
||||||
|
"defaultError": "Что-то пошло не так...",
|
||||||
|
"noDataToUpdate": "Нет данных для обновления.",
|
||||||
|
"loginFirst": "Войдите, чтобы выполнить это действие!"
|
||||||
|
},
|
||||||
|
"success": {
|
||||||
|
"login": "Вход выполнен",
|
||||||
|
"register": "Аккаунт успешно создан. Пожалуйста, подтвердите ваш Email перед входом!",
|
||||||
|
"confirmEmail": "Ссылка для подтверждения E-mail успешно отправлена!",
|
||||||
|
"reset": "Если указанный email существует в нашей системе, мы отправим на него письмо для восстановления пароля!",
|
||||||
|
"newPassword": "Вы успешно изменили пароль!",
|
||||||
|
"contactUs": "Ваше сообщение успешно отправлено!",
|
||||||
|
"addToCart": "{product} добавлен в корзину!",
|
||||||
|
"removeFromCart": "{product} удален из корзины!",
|
||||||
|
"removeAllFromCart": "Корзина успешно очищена!",
|
||||||
|
"addToWishlist": "{product} добавлен в избранное!",
|
||||||
|
"removeFromWishlist": "{product} удален из избранного!",
|
||||||
|
"removeAllFromWishlist": "Список избранного успешно очищен!",
|
||||||
|
"bulkRemoveWishlist": "Выбранные товары удалены из избранного!",
|
||||||
|
"avatarUpload": "Аватар успешно загружен!",
|
||||||
|
"userUpdate": "Профиль успешно обновлен!",
|
||||||
|
"emailUpdate": "Проверьте вашу почту для перехода по ссылке подтверждения и завершения обновления email.",
|
||||||
|
"referralCopy": "Реферальная ссылка скопирована!",
|
||||||
|
"promocodeCopy": "Промокод скопирован!",
|
||||||
|
"configCopy": "Новая ui конфигурация скопирована!",
|
||||||
|
"addFeedback": "Ваш отзыв сохранен!"
|
||||||
|
},
|
||||||
|
"addToCartLimit": "Лимит общего количества составляет {quantity}!",
|
||||||
|
"failAdd": "Пожалуйста, войдите, чтобы совершить покупку",
|
||||||
|
"activationSuccess": "E-mail успешно подтвержден. Пожалуйста, войдите!",
|
||||||
|
"successUpdate": "Профиль успешно обновлен!",
|
||||||
|
"payment": "Ваш платеж обрабатывается! Пожалуйста, подождите",
|
||||||
|
"successCheckout": "Заказ успешно оплачен!"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"nav": {
|
||||||
|
"shop": "Магазин",
|
||||||
|
"catalog": "Каталог",
|
||||||
|
"brands": "Бренды",
|
||||||
|
"blog": "Блог",
|
||||||
|
"contact": "Контакты"
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"wishlist": "Избранное",
|
||||||
|
"cart": "Корзина",
|
||||||
|
"login": "Войти",
|
||||||
|
"profile": "Профиль"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"empty": "Ничего не найдено"
|
||||||
|
},
|
||||||
|
"catalog": {
|
||||||
|
"title": "Каталог"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"address": "Адрес: ",
|
||||||
|
"email": "Email: ",
|
||||||
|
"phone": "Телефон: ",
|
||||||
|
"text": "Курируем вневременную роскошную моду для взыскательных личностей",
|
||||||
|
"shop": "Магазин",
|
||||||
|
"allProducts": "Все товары",
|
||||||
|
"catalog": "Каталог",
|
||||||
|
"brands": "Бренды",
|
||||||
|
"help": "Помощь",
|
||||||
|
"rights": "Все права защищены."
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"hero": {
|
||||||
|
"title": "Вневременная элегантность",
|
||||||
|
"text": "Откройте для себя нашу кураторскую коллекцию люксовой моды и аксессуаров, определяющих утонченный стиль"
|
||||||
|
},
|
||||||
|
"categories": {
|
||||||
|
"title": "Покупки по категориям"
|
||||||
|
},
|
||||||
|
"ad": {
|
||||||
|
"title": "Весенняя коллекция",
|
||||||
|
"text1": "Скидки до 40%",
|
||||||
|
"text2": "Откройте для себя последние тренды в мире люксовой моды"
|
||||||
|
},
|
||||||
|
"blog": {
|
||||||
|
"title": "Из журнала"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forms": {
|
||||||
|
"login": {
|
||||||
|
"title": "С возвращением",
|
||||||
|
"subtitle": "Войдите в свой аккаунт, чтобы продолжить",
|
||||||
|
"forgot": "Забыли пароль?",
|
||||||
|
"or": "или"
|
||||||
|
},
|
||||||
|
"register": {
|
||||||
|
"title": "Создать аккаунт",
|
||||||
|
"subtitle": "Зарегистрируйтесь, чтобы начать ваше стильное путешествие",
|
||||||
|
"login": "Уже есть аккаунт?"
|
||||||
|
},
|
||||||
|
"reset": {
|
||||||
|
"title": "Сброс пароля",
|
||||||
|
"subtitle": "Введите ваш email, и мы отправим ссылку для сброса пароля.",
|
||||||
|
"backToLogin": "Назад к входу"
|
||||||
|
},
|
||||||
|
"newPassword": {
|
||||||
|
"title": "Новый пароль"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cards": {
|
||||||
|
"product": {
|
||||||
|
"stock": "В наличии: "
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"breadcrumbs": {
|
||||||
|
"home": "Главная",
|
||||||
|
"catalog": "Каталог",
|
||||||
|
"contact": "Контакты",
|
||||||
|
"orders": "Заказы",
|
||||||
|
"wishlist": "Избранное",
|
||||||
|
"cart": "Корзина",
|
||||||
|
"settings": "Настройки",
|
||||||
|
"balance": "Баланс",
|
||||||
|
"promocodes": "Промокоды",
|
||||||
|
"login": "Вход",
|
||||||
|
"register": "Регистрация",
|
||||||
|
"resetPassword": "Сброс пароля",
|
||||||
|
"newPassword": "Новый пароль",
|
||||||
|
"brands": "Бренды",
|
||||||
|
"blog": "Блог",
|
||||||
|
"search": "Поиск",
|
||||||
|
"categories": "Категории",
|
||||||
|
"shop": "Магазин",
|
||||||
|
"policy": "Политика конфиденциальности",
|
||||||
|
"terms": "Условия использования",
|
||||||
|
"return": "Политика возврата",
|
||||||
|
"faq": "Часто задаваемые вопросы",
|
||||||
|
"shipping": "Информация о доставке",
|
||||||
|
"about": "О нас"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"title": "Свяжитесь с нами",
|
||||||
|
"text": "Мы будем рады услышать вас. Отправьте нам сообщение, и мы ответим как можно скорее.",
|
||||||
|
"block": {
|
||||||
|
"title": "Давайте общаться",
|
||||||
|
"text": "Есть ли у вас вопросы о наших товарах, нужен совет по стилю или вы хотите сотрудничать с нами — мы здесь, чтобы помочь. Наша команда стремится предоставлять исключительный сервис и поддержку.",
|
||||||
|
"email": "Написать нам",
|
||||||
|
"call": "Позвонить нам",
|
||||||
|
"hours": "Часы работы"
|
||||||
|
},
|
||||||
|
"form": {
|
||||||
|
"title": "Отправить сообщение"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"store": {
|
||||||
|
"sorting": "Сортировать по:",
|
||||||
|
"filters": {
|
||||||
|
"title": "Фильтры",
|
||||||
|
"apply": "Применить",
|
||||||
|
"reset": "Сбросить",
|
||||||
|
"all": "Все"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"title": "Результаты поиска",
|
||||||
|
"products": "Товары",
|
||||||
|
"categories": "Категории",
|
||||||
|
"brands": "Бренды",
|
||||||
|
"byRequest": "по запросу"
|
||||||
|
},
|
||||||
|
"product": {
|
||||||
|
"characteristics": "Все характеристики",
|
||||||
|
"similar": "Вам также может понравиться"
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Настройки",
|
||||||
|
"joinData": "Дата регистрации",
|
||||||
|
"accountInfo": "Информация об аккаунте",
|
||||||
|
"copyReferral": "Скопировать реферальную ссылку",
|
||||||
|
"referralTooltip": "Вы получите реферальную ссылку после успешной покупки"
|
||||||
|
},
|
||||||
|
"orders": {
|
||||||
|
"title": "Заказы",
|
||||||
|
"chooseStatus": "Выберите статус",
|
||||||
|
"id": "№",
|
||||||
|
"price": "Цена",
|
||||||
|
"total": "Итого",
|
||||||
|
"empty": "Нет заказов по данным параметрам.",
|
||||||
|
"statuses": {
|
||||||
|
"all": "Все",
|
||||||
|
"failed": "Ошибка",
|
||||||
|
"payment": "Ожидает оплаты",
|
||||||
|
"created": "Создан",
|
||||||
|
"delivering": "Доставляется",
|
||||||
|
"finished": "Завершен",
|
||||||
|
"momental": "Моментальный"
|
||||||
|
},
|
||||||
|
"searchTooltip": "Введите номер заказа или название товара"
|
||||||
|
},
|
||||||
|
"wishlist": {
|
||||||
|
"title": "Избранное",
|
||||||
|
"total": "{quantity} товаров на сумму {amount}",
|
||||||
|
"deleteTooltip": "Удалить всё из избранного",
|
||||||
|
"empty": "Ваш список избранного пуст."
|
||||||
|
},
|
||||||
|
"cart": {
|
||||||
|
"title": "Корзина",
|
||||||
|
"quantity": "Количество: ",
|
||||||
|
"total": "Итого",
|
||||||
|
"empty": "Ваша корзина пуста."
|
||||||
|
},
|
||||||
|
"balance": {
|
||||||
|
"title": "Баланс"
|
||||||
|
},
|
||||||
|
"promocodes": {
|
||||||
|
"title": "Промокоды",
|
||||||
|
"until": "До",
|
||||||
|
"empty": "У вас нет промокодов."
|
||||||
|
},
|
||||||
|
"logout": "Выйти"
|
||||||
|
},
|
||||||
|
"demo": {
|
||||||
|
"settings": {
|
||||||
|
"title": "Демо-настройки",
|
||||||
|
"ui": "Настройки интерфейса"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"reset": "Сбросить на умолчания",
|
||||||
|
"save": "Сохранить изменения",
|
||||||
|
"generateCode": "Сгенерировать код для 'app.config.ts'"
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"text": "Замените объект UI в 'app.config.ts' на этот код"
|
||||||
|
},
|
||||||
|
"descriptions": {
|
||||||
|
"showBreadcrumbs": "Показывать цепочку навигации на страницах.",
|
||||||
|
"showSearchBar": "Показывать строку поиска в шапке сайта."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"docs": {
|
||||||
|
"faq": {
|
||||||
|
"title": "Часто задаваемые вопросы"
|
||||||
|
},
|
||||||
|
"shipping": {
|
||||||
|
"title": "Информация о доставке"
|
||||||
|
},
|
||||||
|
"return": {
|
||||||
|
"title": "Политика возврата"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"title": "Политика конфиденциальности"
|
||||||
|
},
|
||||||
|
"terms": {
|
||||||
|
"title": "Условия использования"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"title": "О нас"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"brands": {
|
||||||
|
"title": "Люксовые бренды",
|
||||||
|
"text": "Откройте для себя самые престижные мировые модные дома и люксовые бренды, подобранные для взыскательного вкуса."
|
||||||
|
},
|
||||||
|
"catalog": {
|
||||||
|
"title": "Разнообразные категории",
|
||||||
|
"text": "Откройте для себя самые престижные мировые модные дома и люксовые категории, подобранные для взыскательного вкуса."
|
||||||
|
},
|
||||||
|
"blog": {
|
||||||
|
"title": "Модный журнал",
|
||||||
|
"text": "Узнавайте о последних трендах, источниках вдохновения для стиля и модных инсайтах от нашей редакции."
|
||||||
|
},
|
||||||
|
"cart": {
|
||||||
|
"title": "Ваша корзина",
|
||||||
|
"items": "нет товаров | {count} товар | {count} товара | {count} товаров"
|
||||||
|
},
|
||||||
|
"shop": {
|
||||||
|
"title": "Магазин",
|
||||||
|
"text": "Откройте для себя нашу кураторскую коллекцию люксовой моды и аксессуаров, определяющих утонченный стиль"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,344 +1 @@
|
||||||
{
|
{}
|
||||||
"buttons": {
|
|
||||||
"login": "Войти",
|
|
||||||
"register": "Регистрация",
|
|
||||||
"createAccount": "Создать аккаунт",
|
|
||||||
"addToCart": "В корзину",
|
|
||||||
"removeFromCart": "Удалить из корзины",
|
|
||||||
"addAllToCart": "Добавить все в корзину",
|
|
||||||
"addToWishlist": "В избранное",
|
|
||||||
"removeFromWishlist": "Удалить из избранного",
|
|
||||||
"send": "Отправить",
|
|
||||||
"goEmail": "Перейти к почте",
|
|
||||||
"logout": "Выйти",
|
|
||||||
"checkout": "Оформить заказ",
|
|
||||||
"save": "Сохранить",
|
|
||||||
"sendLink": "Отправить ссылку для сброса",
|
|
||||||
"topUp": "Пополнить",
|
|
||||||
"shopNow": "КУПИТЬ СЕЙЧАС",
|
|
||||||
"shopTheSale": "Купить по акции",
|
|
||||||
"readMore": "Читать далее",
|
|
||||||
"sendMessage": "Отправить сообщение",
|
|
||||||
"saveChanges": "Сохранить изменения",
|
|
||||||
"clearAll": "Очистить всё"
|
|
||||||
},
|
|
||||||
"errors": {
|
|
||||||
"required": "Это поле обязательно!",
|
|
||||||
"mail": "Введите корректный email!",
|
|
||||||
"compare": "Пароли не совпадают!",
|
|
||||||
"needLower": "Добавьте строчную букву.",
|
|
||||||
"needUpper": "Добавьте заглавную букву.",
|
|
||||||
"needNumber": "Добавьте цифру.",
|
|
||||||
"needMin": "Мин. 8 символов",
|
|
||||||
"needSpecial": "Добавьте спецсимвол: #.?!$%^&*'()_+=:;\"'/>.<,|\\-",
|
|
||||||
"pageNotFound": "Страница не найдена"
|
|
||||||
},
|
|
||||||
"fields": {
|
|
||||||
"search": "Поиск",
|
|
||||||
"searchOrder": "Поиск заказа",
|
|
||||||
"name": "Имя",
|
|
||||||
"firstName": "Имя",
|
|
||||||
"lastName": "Фамилия",
|
|
||||||
"phoneNumber": "Номер телефона",
|
|
||||||
"email": "Email",
|
|
||||||
"subject": "Тема",
|
|
||||||
"message": "Ваше сообщение",
|
|
||||||
"password": "Пароль",
|
|
||||||
"newPassword": "Новый пароль",
|
|
||||||
"confirmPassword": "Подтвердите пароль",
|
|
||||||
"confirmNewPassword": "Подтвердите новый пароль",
|
|
||||||
"brandsSearch": "Поиск брендов по названию..."
|
|
||||||
},
|
|
||||||
"checkboxes": {
|
|
||||||
"remember": "Запомнить меня",
|
|
||||||
"chooseAll": "Выбрать все",
|
|
||||||
"agree": "Я согласен с {terms} и {policy}",
|
|
||||||
"subscribe": "Подписаться на рассылку новостей об эксклюзивных предложениях и обновлениях"
|
|
||||||
},
|
|
||||||
"popup": {
|
|
||||||
"errors": {
|
|
||||||
"main": "Ошибка!",
|
|
||||||
"defaultError": "Что-то пошло не так...",
|
|
||||||
"noDataToUpdate": "Нет данных для обновления.",
|
|
||||||
"loginFirst": "Войдите, чтобы выполнить это действие!",
|
|
||||||
"alreadyInWishlist": "{product} уже есть в избранном!"
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
"login": "Вход выполнен",
|
|
||||||
"register": "Аккаунт успешно создан. Пожалуйста, подтвердите ваш Email перед входом!",
|
|
||||||
"confirmEmail": "Ссылка для подтверждения E-mail успешно отправлена!",
|
|
||||||
"reset": "Если указанный email существует в нашей системе, мы отправим на него письмо для восстановления пароля!",
|
|
||||||
"newPassword": "Вы успешно изменили пароль!",
|
|
||||||
"contactUs": "Ваше сообщение успешно отправлено!",
|
|
||||||
"addToCart": "{product} добавлен в корзину!",
|
|
||||||
"removeFromCart": "{product} удален из корзины!",
|
|
||||||
"removeAllFromCart": "Корзина успешно очищена!",
|
|
||||||
"addToWishlist": "{product} добавлен в избранное!",
|
|
||||||
"removeFromWishlist": "{product} удален из избранного!",
|
|
||||||
"removeAllFromWishlist": "Список избранного успешно очищен!",
|
|
||||||
"bulkRemoveWishlist": "Выбранные товары удалены из избранного!",
|
|
||||||
"bulkRemoveOrder": "Выбранные товары удалены из корзины!",
|
|
||||||
"avatarUpload": "Аватар успешно загружен!",
|
|
||||||
"userUpdate": "Профиль успешно обновлен!",
|
|
||||||
"emailUpdate": "Проверьте вашу почту для перехода по ссылке подтверждения и завершения обновления email.",
|
|
||||||
"referralCopy": "Реферальная ссылка скопирована!",
|
|
||||||
"promocodeCopy": "Промокод скопирован!",
|
|
||||||
"configCopy": "Новая ui конфигурация скопирована!",
|
|
||||||
"addFeedback": "Ваш отзыв сохранен!"
|
|
||||||
},
|
|
||||||
"addToCartLimit": "Лимит общего количества составляет {quantity}!",
|
|
||||||
"failAdd": "Пожалуйста, войдите, чтобы совершить покупку",
|
|
||||||
"activationSuccess": "E-mail успешно подтвержден. Пожалуйста, войдите!",
|
|
||||||
"successUpdate": "Профиль успешно обновлен!",
|
|
||||||
"payment": "Ваш платеж обрабатывается! Пожалуйста, подождите",
|
|
||||||
"successCheckout": "Заказ успешно оплачен!"
|
|
||||||
},
|
|
||||||
"header": {
|
|
||||||
"nav": {
|
|
||||||
"shop": "Магазин",
|
|
||||||
"catalog": "Каталог",
|
|
||||||
"brands": "Бренды",
|
|
||||||
"blog": "Блог",
|
|
||||||
"contact": "Контакты"
|
|
||||||
},
|
|
||||||
"actions": {
|
|
||||||
"wishlist": "Избранное",
|
|
||||||
"cart": "Корзина",
|
|
||||||
"login": "Войти",
|
|
||||||
"profile": "Профиль"
|
|
||||||
},
|
|
||||||
"search": {
|
|
||||||
"empty": "Ничего не найдено"
|
|
||||||
},
|
|
||||||
"catalog": {
|
|
||||||
"title": "Каталог"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"footer": {
|
|
||||||
"address": "Адрес: ",
|
|
||||||
"email": "Email: ",
|
|
||||||
"phone": "Телефон: ",
|
|
||||||
"text": "Курируем вневременную роскошную моду для взыскательных личностей",
|
|
||||||
"shop": "Магазин",
|
|
||||||
"allProducts": "Все товары",
|
|
||||||
"catalog": "Каталог",
|
|
||||||
"brands": "Бренды",
|
|
||||||
"help": "Помощь",
|
|
||||||
"rights": "Все права защищены."
|
|
||||||
},
|
|
||||||
"home": {
|
|
||||||
"hero": {
|
|
||||||
"title": "Вневременная элегантность",
|
|
||||||
"text": "Откройте для себя нашу кураторскую коллекцию люксовой моды и аксессуаров, определяющих утонченный стиль"
|
|
||||||
},
|
|
||||||
"categories": {
|
|
||||||
"title": "Покупки по категориям"
|
|
||||||
},
|
|
||||||
"ad": {
|
|
||||||
"title": "Весенняя коллекция",
|
|
||||||
"text1": "Скидки до 40%",
|
|
||||||
"text2": "Откройте для себя последние тренды в мире люксовой моды"
|
|
||||||
},
|
|
||||||
"blog": {
|
|
||||||
"title": "Из журнала"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"forms": {
|
|
||||||
"login": {
|
|
||||||
"title": "С возвращением",
|
|
||||||
"subtitle": "Войдите в свой аккаунт, чтобы продолжить",
|
|
||||||
"forgot": "Забыли пароль?",
|
|
||||||
"or": "или"
|
|
||||||
},
|
|
||||||
"register": {
|
|
||||||
"title": "Создать аккаунт",
|
|
||||||
"subtitle": "Зарегистрируйтесь, чтобы начать ваше стильное путешествие",
|
|
||||||
"login": "Уже есть аккаунт?"
|
|
||||||
},
|
|
||||||
"reset": {
|
|
||||||
"title": "Сброс пароля",
|
|
||||||
"subtitle": "Введите ваш email, и мы отправим ссылку для сброса пароля.",
|
|
||||||
"backToLogin": "Назад к входу"
|
|
||||||
},
|
|
||||||
"newPassword": {
|
|
||||||
"title": "Новый пароль"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cards": {
|
|
||||||
"product": {
|
|
||||||
"stock": "В наличии: "
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"breadcrumbs": {
|
|
||||||
"home": "Главная",
|
|
||||||
"catalog": "Каталог",
|
|
||||||
"contact": "Контакты",
|
|
||||||
"orders": "Заказы",
|
|
||||||
"wishlist": "Избранное",
|
|
||||||
"cart": "Корзина",
|
|
||||||
"settings": "Настройки",
|
|
||||||
"balance": "Баланс",
|
|
||||||
"promocodes": "Промокоды",
|
|
||||||
"login": "Вход",
|
|
||||||
"register": "Регистрация",
|
|
||||||
"resetPassword": "Сброс пароля",
|
|
||||||
"newPassword": "Новый пароль",
|
|
||||||
"brands": "Бренды",
|
|
||||||
"blog": "Блог",
|
|
||||||
"search": "Поиск",
|
|
||||||
"categories": "Категории",
|
|
||||||
"shop": "Магазин",
|
|
||||||
"policy": "Политика конфиденциальности",
|
|
||||||
"terms": "Условия использования",
|
|
||||||
"return": "Политика возврата",
|
|
||||||
"faq": "Часто задаваемые вопросы",
|
|
||||||
"shipping": "Информация о доставке",
|
|
||||||
"about": "О нас"
|
|
||||||
},
|
|
||||||
"contact": {
|
|
||||||
"title": "Свяжитесь с нами",
|
|
||||||
"text": "Мы будем рады услышать вас. Отправьте нам сообщение, и мы ответим как можно скорее.",
|
|
||||||
"block": {
|
|
||||||
"title": "Давайте общаться",
|
|
||||||
"text": "Есть ли у вас вопросы о наших товарах, нужен совет по стилю или вы хотите сотрудничать с нами — мы здесь, чтобы помочь. Наша команда стремится предоставлять исключительный сервис и поддержку.",
|
|
||||||
"email": "Написать нам",
|
|
||||||
"call": "Позвонить нам",
|
|
||||||
"hours": "Часы работы"
|
|
||||||
},
|
|
||||||
"form": {
|
|
||||||
"title": "Отправить сообщение"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"store": {
|
|
||||||
"sorting": "Сортировать по:",
|
|
||||||
"filters": {
|
|
||||||
"title": "Фильтры",
|
|
||||||
"apply": "Применить",
|
|
||||||
"reset": "Сбросить",
|
|
||||||
"all": "Все"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"search": {
|
|
||||||
"title": "Результаты поиска",
|
|
||||||
"products": "Товары",
|
|
||||||
"categories": "Категории",
|
|
||||||
"brands": "Бренды",
|
|
||||||
"byRequest": "по запросу"
|
|
||||||
},
|
|
||||||
"product": {
|
|
||||||
"characteristics": "Все характеристики",
|
|
||||||
"similar": "Вам также может понравиться"
|
|
||||||
},
|
|
||||||
"profile": {
|
|
||||||
"settings": {
|
|
||||||
"title": "Настройки",
|
|
||||||
"joinData": "Дата регистрации",
|
|
||||||
"accountInfo": "Информация об аккаунте",
|
|
||||||
"copyReferral": "Скопировать реферальную ссылку",
|
|
||||||
"referralTooltip": "Вы получите реферальную ссылку после успешной покупки"
|
|
||||||
},
|
|
||||||
"orders": {
|
|
||||||
"title": "Заказы",
|
|
||||||
"chooseStatus": "Выберите статус",
|
|
||||||
"id": "№",
|
|
||||||
"price": "Цена",
|
|
||||||
"total": "Итого",
|
|
||||||
"empty": "Нет заказов по данным параметрам.",
|
|
||||||
"statuses": {
|
|
||||||
"all": "Все",
|
|
||||||
"failed": "Ошибка",
|
|
||||||
"payment": "Ожидает оплаты",
|
|
||||||
"created": "Создан",
|
|
||||||
"delivering": "Доставляется",
|
|
||||||
"finished": "Завершен",
|
|
||||||
"momental": "Моментальный"
|
|
||||||
},
|
|
||||||
"searchTooltip": "Введите номер заказа или название товара"
|
|
||||||
},
|
|
||||||
"wishlist": {
|
|
||||||
"title": "Избранное",
|
|
||||||
"total": "{quantity} товаров на сумму {amount}",
|
|
||||||
"deleteTooltip": "Удалить всё из избранного",
|
|
||||||
"empty": "Ваш список избранного пуст."
|
|
||||||
},
|
|
||||||
"cart": {
|
|
||||||
"title": "Корзина",
|
|
||||||
"quantity": "Количество: ",
|
|
||||||
"total": "Итого",
|
|
||||||
"empty": "Ваша корзина пуста."
|
|
||||||
},
|
|
||||||
"balance": {
|
|
||||||
"title": "Баланс"
|
|
||||||
},
|
|
||||||
"promocodes": {
|
|
||||||
"title": "Промокоды",
|
|
||||||
"until": "До",
|
|
||||||
"empty": "У вас нет промокодов."
|
|
||||||
},
|
|
||||||
"logout": "Выйти"
|
|
||||||
},
|
|
||||||
"demo": {
|
|
||||||
"settings": {
|
|
||||||
"title": "Демо-настройки",
|
|
||||||
"ui": "Настройки интерфейса"
|
|
||||||
},
|
|
||||||
"buttons": {
|
|
||||||
"reset": "Сбросить на умолчания",
|
|
||||||
"save": "Сохранить изменения",
|
|
||||||
"generateCode": "Сгенерировать код для 'app.config.ts'"
|
|
||||||
},
|
|
||||||
"preview": {
|
|
||||||
"text": "Замените объект UI в 'app.config.ts' на этот код"
|
|
||||||
},
|
|
||||||
"descriptions": {
|
|
||||||
"showBreadcrumbs": "Показывать цепочку навигации на страницах.",
|
|
||||||
"showSearchBar": "Показывать строку поиска в шапке сайта."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"docs": {
|
|
||||||
"faq": {
|
|
||||||
"title": "Часто задаваемые вопросы"
|
|
||||||
},
|
|
||||||
"shipping": {
|
|
||||||
"title": "Информация о доставке"
|
|
||||||
},
|
|
||||||
"return": {
|
|
||||||
"title": "Политика возврата"
|
|
||||||
},
|
|
||||||
"policy": {
|
|
||||||
"title": "Политика конфиденциальности"
|
|
||||||
},
|
|
||||||
"terms": {
|
|
||||||
"title": "Условия использования"
|
|
||||||
},
|
|
||||||
"about": {
|
|
||||||
"title": "О нас"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"brands": {
|
|
||||||
"title": "Люксовые бренды",
|
|
||||||
"text": "Откройте для себя самые престижные мировые модные дома и люксовые бренды, подобранные для взыскательного вкуса."
|
|
||||||
},
|
|
||||||
"catalog": {
|
|
||||||
"title": "Разнообразные категории",
|
|
||||||
"text": "Откройте для себя самые престижные мировые модные дома и люксовые категории, подобранные для взыскательного вкуса."
|
|
||||||
},
|
|
||||||
"blog": {
|
|
||||||
"title": "Модный журнал",
|
|
||||||
"text": "Узнавайте о последних трендах, источниках вдохновения для стиля и модных инсайтах от нашей редакции."
|
|
||||||
},
|
|
||||||
"cart": {
|
|
||||||
"title": "Моя корзина",
|
|
||||||
"items": "нет товаров | {count} товар | {count} товара | {count} товаров",
|
|
||||||
"empty": "Ваша корзина пуста"
|
|
||||||
},
|
|
||||||
"wishlist": {
|
|
||||||
"title": "Мои Избранные",
|
|
||||||
"items": "нет товаров | {count} товар | {count} товара | {count} товаров",
|
|
||||||
"empty": "Список выших избранных пуст"
|
|
||||||
},
|
|
||||||
"shop": {
|
|
||||||
"title": "Магазин",
|
|
||||||
"text": "Откройте для себя нашу кураторскую коллекцию люксовой моды и аксессуаров, определяющих утонченный стиль"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue