Features: 1) Introduce handleDeposit function with validation logic and deposit transaction flow; 2) Add useDeposit composable and balance.vue page for user account balance management; 3) Enhance wishlist and cart functionality with authentication checks and notification improvements;
Fixes: 1) Replace `ElNotification` with `useNotification` across all components and composables; 2) Add missing semicolons, consistent formatting, and type annotations in multiple files; 3) Resolve non-reactive elements in wishlist and cart state management; Extra: 1) Update i18n translations with new strings for promocodes, balance, authentication, and profile settings; 2) Refactor SCSS styles including variable additions and component-specific tweaks; 3) Remove redundant queries, unused imports, and `storePage.ts` file for cleanup.
This commit is contained in:
parent
761fecf67f
commit
c60ac13e88
62 changed files with 1560 additions and 563 deletions
|
|
@ -68,12 +68,20 @@ await Promise.all([
|
||||||
watch(
|
watch(
|
||||||
() => appStore.activeState,
|
() => appStore.activeState,
|
||||||
(state) => {
|
(state) => {
|
||||||
appStore.setOverflowHidden(state !== '')
|
appStore.setOverflowHidden(state !== '');
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
);
|
||||||
|
|
||||||
let stopWatcher: VoidFunction = () => {}
|
watch(locale, () => {
|
||||||
|
useHead({
|
||||||
|
htmlAttrs: {
|
||||||
|
lang: locale.value
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let stopWatcher: VoidFunction = () => {};
|
||||||
|
|
||||||
onMounted( async () => {
|
onMounted( async () => {
|
||||||
refreshInterval = setInterval(async () => {
|
refreshInterval = setInterval(async () => {
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,6 @@ $accentDark: #5743b5;
|
||||||
$accentLight: #a69cdc;
|
$accentLight: #a69cdc;
|
||||||
$accentDisabled: #826fa2;
|
$accentDisabled: #826fa2;
|
||||||
$accentSmooth: #656bd1;
|
$accentSmooth: #656bd1;
|
||||||
|
$contrast: #FFC107;
|
||||||
$error: #f13838;
|
$error: #f13838;
|
||||||
$default_border_radius: 4px;
|
$default_border_radius: 4px;
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
@click="appStore.setActiveState('login')"
|
@click="appStore.setActiveState('login')"
|
||||||
v-else
|
v-else
|
||||||
>
|
>
|
||||||
<icon name="material-symbols-light:person-outline-rounded" size="32" />
|
<icon name="material-symbols-light:person-outline-rounded" size="32" />
|
||||||
<p>{{ t('header.actions.login') }}</p>
|
<p>{{ t('header.actions.login') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<template #fallback>
|
<template #fallback>
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
class="header__actions-item"
|
class="header__actions-item"
|
||||||
@click="appStore.setActiveState('login')"
|
@click="appStore.setActiveState('login')"
|
||||||
>
|
>
|
||||||
<icon name="material-symbols-light:person-outline-rounded" size="32" />
|
<icon name="material-symbols-light:person-outline-rounded" size="32" />
|
||||||
<p>{{ t('header.actions.login') }}</p>
|
<p>{{ t('header.actions.login') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -58,8 +58,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {useLogout} from "~/composables/auth";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
@ -79,8 +77,6 @@ const productsInCartQuantity = computed(() => {
|
||||||
const productsInWishlistQuantity = computed(() => {
|
const productsInWishlistQuantity = computed(() => {
|
||||||
return wishlistStore.wishlist ? wishlistStore.wishlist.products.edges.length : 0;
|
return wishlistStore.wishlist ? wishlistStore.wishlist.products.edges.length : 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { logout } = useLogout();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
type="text"
|
type="text"
|
||||||
v-model="query"
|
v-model="query"
|
||||||
:placeholder="t('fields.search')"
|
:placeholder="t('fields.search')"
|
||||||
|
inputmode="search"
|
||||||
/>
|
/>
|
||||||
<div class="search__tools">
|
<div class="search__tools">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nuxt-link-locale>
|
</nuxt-link-locale>
|
||||||
<div class="card__content">
|
<div class="card__content">
|
||||||
<div class="card__price">{{ product.price }}</div>
|
<div class="card__price">{{ product.price }} {{ CURRENCY }}</div>
|
||||||
<p class="card__name">{{ product.name }}</p>
|
<p class="card__name">{{ product.name }}</p>
|
||||||
<el-rate
|
<el-rate
|
||||||
v-model="rating"
|
v-model="rating"
|
||||||
|
|
@ -58,40 +58,65 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card__bottom">
|
<div class="card__bottom">
|
||||||
<ui-button
|
<div class="card__bottom-inner">
|
||||||
class="card__bottom-button"
|
<ui-button
|
||||||
v-if="isProductInCart"
|
class="card__bottom-button"
|
||||||
@click="overwriteOrder({
|
v-if="isProductInCart"
|
||||||
type: 'remove',
|
@click="overwriteOrder({
|
||||||
productUuid: product.uuid,
|
type: 'remove',
|
||||||
productName: product.name
|
productUuid: product.uuid,
|
||||||
})"
|
productName: product.name
|
||||||
:isLoading="removeLoading"
|
})"
|
||||||
>
|
:isLoading="removeLoading"
|
||||||
{{ t('buttons.removeFromCart') }}
|
>
|
||||||
</ui-button>
|
{{ t('buttons.removeFromCart') }}
|
||||||
<ui-button
|
</ui-button>
|
||||||
v-else
|
<ui-button
|
||||||
class="card__bottom-button"
|
v-else
|
||||||
@click="overwriteOrder({
|
class="card__bottom-button"
|
||||||
type: 'add',
|
@click="overwriteOrder({
|
||||||
productUuid: product.uuid,
|
type: 'add',
|
||||||
productName: product.name
|
productUuid: product.uuid,
|
||||||
})"
|
productName: product.name
|
||||||
:isLoading="addLoading"
|
})"
|
||||||
>
|
:isLoading="addLoading"
|
||||||
{{ t('buttons.addToCart') }}
|
>
|
||||||
</ui-button>
|
{{ t('buttons.addToCart') }}
|
||||||
<div
|
</ui-button>
|
||||||
class="card__bottom-wishlist"
|
<div
|
||||||
@click="overwriteWishlist({
|
class="card__bottom-wishlist"
|
||||||
type: (isProductInWishlist ? 'remove' : 'add'),
|
@click="overwriteWishlist({
|
||||||
productUuid: product.uuid,
|
type: (isProductInWishlist ? 'remove' : 'add'),
|
||||||
productName: product.name
|
productUuid: product.uuid,
|
||||||
})"
|
productName: product.name
|
||||||
>
|
})"
|
||||||
<icon name="mdi:cards-heart" size="28" v-if="isProductInWishlist" />
|
>
|
||||||
<icon name="mdi:cards-heart-outline" size="28" v-else />
|
<icon name="mdi:cards-heart" size="28" v-if="isProductInWishlist" />
|
||||||
|
<icon name="mdi:cards-heart-outline" size="28" v-else />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tools" v-if="isToolsVisible && isProductInCart">
|
||||||
|
<button
|
||||||
|
class="tools__item tools__item-button"
|
||||||
|
@click="overwriteOrder({
|
||||||
|
type: 'remove',
|
||||||
|
productUuid: product.uuid,
|
||||||
|
productName: product.name
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
-
|
||||||
|
</button>
|
||||||
|
<span class="tools__item tools__item-count" v-text="'X' + productinCartQuantity" />
|
||||||
|
<button
|
||||||
|
class="tools__item tools__item-button"
|
||||||
|
@click="overwriteOrder({
|
||||||
|
type: 'add',
|
||||||
|
productUuid: product.uuid,
|
||||||
|
productName: product.name
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -106,10 +131,12 @@ import 'swiper/css/effect-fade';
|
||||||
import 'swiper/css/pagination'
|
import 'swiper/css/pagination'
|
||||||
import {useWishlistOverwrite} from "~/composables/wishlist";
|
import {useWishlistOverwrite} from "~/composables/wishlist";
|
||||||
import {useOrderOverwrite} from "~/composables/orders/useOrderOverwrite";
|
import {useOrderOverwrite} from "~/composables/orders/useOrderOverwrite";
|
||||||
|
import {CURRENCY} from "~/config/constants";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
product: IProduct;
|
product: IProduct;
|
||||||
isList?: boolean;
|
isList?: boolean;
|
||||||
|
isToolsVisible?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
|
|
@ -129,6 +156,9 @@ const isProductInWishlist = computed(() => {
|
||||||
const isProductInCart = computed(() => {
|
const isProductInCart = computed(() => {
|
||||||
return cartStore.currentOrder?.orderProducts?.edges.find((prod) => prod.node.product.uuid === props.product?.uuid);
|
return cartStore.currentOrder?.orderProducts?.edges.find((prod) => prod.node.product.uuid === props.product?.uuid);
|
||||||
});
|
});
|
||||||
|
const productinCartQuantity = computed(() => {
|
||||||
|
return cartStore.currentOrder?.orderProducts?.edges.filter((prod) => prod.node.product.uuid === props.product.uuid)[0].node.quantity;
|
||||||
|
});
|
||||||
|
|
||||||
const rating = computed(() => {
|
const rating = computed(() => {
|
||||||
return props.product.feedbacks.edges[0]?.node?.rating ?? 5;
|
return props.product.feedbacks.edges[0]?.node?.rating ?? 5;
|
||||||
|
|
@ -173,7 +203,7 @@ function goTo(index: number) {
|
||||||
|
|
||||||
&__list {
|
&__list {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: flex-start;
|
align-items: stretch;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
|
|
@ -191,10 +221,17 @@ function goTo(index: number) {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding-inline: 0;
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
gap: 10px;
|
|
||||||
|
&-inner {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&-button {
|
&-button {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
|
@ -286,7 +323,7 @@ function goTo(index: number) {
|
||||||
|
|
||||||
&__quantity {
|
&__quantity {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
background-color: rgba($accent, 0.2);
|
background-color: rgba($contrast, 0.5);
|
||||||
border-radius: $default_border_radius;
|
border-radius: $default_border_radius;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
@ -295,12 +332,15 @@ function goTo(index: number) {
|
||||||
&__bottom {
|
&__bottom {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
padding: 0 20px 20px 20px;
|
padding: 0 20px 20px 20px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 5px;
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
|
&-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
&-button {
|
&-button {
|
||||||
width: 84%;
|
width: 84%;
|
||||||
}
|
}
|
||||||
|
|
@ -326,6 +366,52 @@ function goTo(index: number) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tools {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: rgba($accent, 0.2);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr 1fr;
|
||||||
|
height: 30px;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
border-left: 1px solid $accent;
|
||||||
|
border-right: 1px solid $accent;
|
||||||
|
|
||||||
|
color: $accent;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba($accent, 0.2);
|
||||||
|
border-radius: 4px 0 0 4px;
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
color: $accent;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
@include hover {
|
||||||
|
background-color: $accent;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.swiper-pagination) {
|
:deep(.swiper-pagination) {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@
|
||||||
:placeholder="t('fields.email')"
|
:placeholder="t('fields.email')"
|
||||||
:rules="[required]"
|
:rules="[required]"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
|
:inputMode="'email'"
|
||||||
/>
|
/>
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
:placeholder="t('fields.phoneNumber')"
|
:placeholder="t('fields.phoneNumber')"
|
||||||
:rules="[required]"
|
:rules="[required]"
|
||||||
v-model="phoneNumber"
|
v-model="phoneNumber"
|
||||||
|
:inputMode="'tel'"
|
||||||
/>
|
/>
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,77 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- <form @submit.prevent="handleDeposit()" class="form">-->
|
<form @submit.prevent="handleDeposit()" class="form">
|
||||||
<form @submit.prevent="" class="form">
|
|
||||||
<div class="form__box">
|
<div class="form__box">
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
:placeholder="''"
|
:placeholder="''"
|
||||||
v-model="amount"
|
v-model="amount"
|
||||||
:numberOnly="true"
|
:numberOnly="true"
|
||||||
|
:inputMode="'decimal'"
|
||||||
/>
|
/>
|
||||||
|
<icon name="ic:baseline-compare-arrows" size="30" />
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
:placeholder="''"
|
:placeholder="''"
|
||||||
v-model="amount"
|
v-model="amount"
|
||||||
:numberOnly="true"
|
:numberOnly="true"
|
||||||
|
:inputMode="'decimal'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- <ui-button-->
|
|
||||||
<!-- class="form__button"-->
|
|
||||||
<!-- :isDisabled="!isFormValid"-->
|
|
||||||
<!-- :isLoading="loading"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- {{ $t('buttons.topUp') }}-->
|
|
||||||
<!-- </ui-button>-->
|
|
||||||
<ui-button
|
<ui-button
|
||||||
class="form__button"
|
class="form__button"
|
||||||
:isDisabled="!isFormValid"
|
:isDisabled="!isFormValid"
|
||||||
|
:isLoading="loading"
|
||||||
>
|
>
|
||||||
{{ t('buttons.topUp') }}
|
{{ t('buttons.topUp') }}
|
||||||
</ui-button>
|
</ui-button>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
// import {useDeposit} from "@/composables/user/useDeposit.js";
|
import {useDeposit} from "~/composables/user";
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n();
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore();
|
||||||
|
|
||||||
const paymentMin = computed(() => companyStore.companyInfo?.paymentGatewayMinimum)
|
const paymentMin = computed(() => companyStore.companyInfo?.paymentGatewayMinimum || 0);
|
||||||
const paymentMax = computed(() => companyStore.companyInfo?.paymentGatewayMaximum)
|
const paymentMax = computed(() => companyStore.companyInfo?.paymentGatewayMaximum || 500);
|
||||||
|
|
||||||
const amount = ref('')
|
const amount = ref<string>("0");
|
||||||
|
|
||||||
const isFormValid = computed(() => {
|
const isFormValid = computed(() => {
|
||||||
return (
|
return (
|
||||||
amount.value >= paymentMin.value &&
|
amount.value >= paymentMin.value &&
|
||||||
amount.value <= paymentMax.value
|
amount.value <= paymentMax.value
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
// const { deposit, loading } = useDeposit();
|
const { deposit, loading } = useDeposit();
|
||||||
//
|
|
||||||
// async function handleDeposit() {
|
async function handleDeposit() {
|
||||||
// await deposit(amount.value);
|
await deposit(amount.value);
|
||||||
// }
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.form {
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
&__box {
|
&__box {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
|
& span {
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
width: fit-content;
|
||||||
|
padding-inline: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
:placeholder="t('fields.email')"
|
:placeholder="t('fields.email')"
|
||||||
:rules="[isEmail]"
|
:rules="[isEmail]"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
|
:inputMode="'email'"
|
||||||
/>
|
/>
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'password'"
|
:type="'password'"
|
||||||
|
|
@ -48,7 +49,7 @@ import {useValidators} from "~/composables/rules";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const { required, isEmail } = useValidators()
|
const { required, isEmail } = useValidators();
|
||||||
|
|
||||||
const email = ref<string>('');
|
const email = ref<string>('');
|
||||||
const password = ref<string>('');
|
const password = ref<string>('');
|
||||||
|
|
@ -58,7 +59,7 @@ const isFormValid = computed(() => {
|
||||||
return (
|
return (
|
||||||
isEmail(email.value) === true &&
|
isEmail(email.value) === true &&
|
||||||
required(password.value) === true
|
required(password.value) === true
|
||||||
)
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const { login, loading } = useLogin();
|
const { login, loading } = useLogin();
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,14 @@
|
||||||
:placeholder="t('fields.phoneNumber')"
|
:placeholder="t('fields.phoneNumber')"
|
||||||
:rules="[required]"
|
:rules="[required]"
|
||||||
v-model="phoneNumber"
|
v-model="phoneNumber"
|
||||||
|
:inputMode="'tel'"
|
||||||
/>
|
/>
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'email'"
|
:type="'email'"
|
||||||
:placeholder="t('fields.email')"
|
:placeholder="t('fields.email')"
|
||||||
:rules="[isEmail]"
|
:rules="[isEmail]"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
|
:inputMode="'email'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ui-input
|
<ui-input
|
||||||
|
|
@ -59,26 +61,29 @@
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import {useValidators} from "~/composables/rules";
|
import {useValidators} from "~/composables/rules";
|
||||||
import {useRegister} from "~/composables/auth/index.js";
|
import {useRegister} from "~/composables/auth/index.js";
|
||||||
|
import {useRouteQuery} from "@vueuse/router";
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n();
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const { required, isEmail, isPasswordValid } = useValidators()
|
const { required, isEmail, isPasswordValid } = useValidators();
|
||||||
|
|
||||||
const firstName = ref('')
|
const firstName = ref<string>('');
|
||||||
const lastName = ref('')
|
const lastName = ref<string>('');
|
||||||
const phoneNumber = ref('')
|
const phoneNumber = ref<string>('');
|
||||||
const email = ref('')
|
const email = ref<string>('');
|
||||||
const password = ref('')
|
const password = ref<string>('');
|
||||||
const confirmPassword = ref('')
|
const confirmPassword = ref<string>('');
|
||||||
|
|
||||||
const compareStrings = (v) => {
|
const referrer = useRouteQuery('referrer', '');
|
||||||
if (v === password.value) return true
|
|
||||||
return t('errors.compare')
|
const compareStrings = (v: string) => {
|
||||||
}
|
if (v === password.value) return true;
|
||||||
|
return t('errors.compare');
|
||||||
|
};
|
||||||
|
|
||||||
const isFormValid = computed(() => {
|
const isFormValid = computed(() => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -88,20 +93,21 @@ const isFormValid = computed(() => {
|
||||||
isEmail(email.value) === true &&
|
isEmail(email.value) === true &&
|
||||||
isPasswordValid(password.value) === true &&
|
isPasswordValid(password.value) === true &&
|
||||||
compareStrings(confirmPassword.value) === true
|
compareStrings(confirmPassword.value) === true
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
const { register, loading } = useRegister();
|
const { register, loading } = useRegister();
|
||||||
|
|
||||||
async function handleRegister() {
|
async function handleRegister() {
|
||||||
await register(
|
await register({
|
||||||
firstName.value,
|
firstName: firstName.value,
|
||||||
lastName.value,
|
lastName: lastName.value,
|
||||||
phoneNumber.value,
|
phoneNumber: phoneNumber.value,
|
||||||
email.value,
|
email: email.value,
|
||||||
password.value,
|
password: password.value,
|
||||||
confirmPassword.value
|
confirmPassword: confirmPassword.value,
|
||||||
);
|
referrer: referrer.value
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -118,7 +124,7 @@ async function handleRegister() {
|
||||||
|
|
||||||
&__box {
|
&__box {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
:placeholder="t('fields.email')"
|
:placeholder="t('fields.email')"
|
||||||
:rules="[isEmail]"
|
:rules="[isEmail]"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
|
:inputMode="'email'"
|
||||||
/>
|
/>
|
||||||
<ui-button
|
<ui-button
|
||||||
class="form__button"
|
class="form__button"
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,98 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- <form class="form" @submit.prevent="handleUpdate()">-->
|
<form class="form" @submit.prevent="handleUpdate">
|
||||||
<form class="form" @submit.prevent="">
|
<div class="form__box">
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
:placeholder="t('fields.firstName')"
|
:placeholder="t('fields.firstName')"
|
||||||
:rules="[required]"
|
:rules="[required]"
|
||||||
v-model="firstName"
|
v-model="firstName"
|
||||||
/>
|
/>
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
:placeholder="t('fields.lastName')"
|
:placeholder="t('fields.lastName')"
|
||||||
:rules="[required]"
|
:rules="[required]"
|
||||||
v-model="lastName"
|
v-model="lastName"
|
||||||
/>
|
/>
|
||||||
<ui-input
|
</div>
|
||||||
:type="'email'"
|
<div class="form__box">
|
||||||
:placeholder="t('fields.email')"
|
<ui-input
|
||||||
:rules="[isEmail]"
|
:type="'email'"
|
||||||
v-model="email"
|
:placeholder="t('fields.email')"
|
||||||
/>
|
:rules="[isEmail]"
|
||||||
<ui-input
|
v-model="email"
|
||||||
:type="'text'"
|
/>
|
||||||
:placeholder="t('fields.phoneNumber')"
|
<ui-input
|
||||||
:rules="[required]"
|
:type="'text'"
|
||||||
v-model="phoneNumber"
|
:placeholder="t('fields.phoneNumber')"
|
||||||
/>
|
:rules="[required]"
|
||||||
<ui-input
|
v-model="phoneNumber"
|
||||||
:type="'password'"
|
/>
|
||||||
:placeholder="t('fields.newPassword')"
|
</div>
|
||||||
:rules="[isPasswordValid]"
|
<div class="form__box">
|
||||||
v-model="password"
|
<ui-input
|
||||||
/>
|
:type="'password'"
|
||||||
<ui-input
|
:placeholder="t('fields.newPassword')"
|
||||||
:type="'password'"
|
:rules="[isPasswordValid]"
|
||||||
:placeholder="t('fields.confirmNewPassword')"
|
v-model="password"
|
||||||
:rules="[compareStrings]"
|
/>
|
||||||
v-model="confirmPassword"
|
<ui-input
|
||||||
/>
|
:type="'password'"
|
||||||
<!-- <ui-button-->
|
:placeholder="t('fields.confirmNewPassword')"
|
||||||
<!-- class="form__button"-->
|
:rules="[compareStrings]"
|
||||||
<!-- :isLoading="loading"-->
|
v-model="confirmPassword"
|
||||||
<!-- >-->
|
/>
|
||||||
<!-- {{ t('buttons.save') }}-->
|
</div>
|
||||||
<!-- </ui-button>-->
|
|
||||||
<ui-button
|
<ui-button
|
||||||
class="form__button"
|
class="form__button"
|
||||||
|
:isLoading="loading"
|
||||||
>
|
>
|
||||||
{{ t('buttons.save') }}
|
{{ t('buttons.save') }}
|
||||||
</ui-button>
|
</ui-button>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import {useValidators} from "~/composables/rules";
|
import {useValidators} from "~/composables/rules";
|
||||||
// import {useUserUpdating} from "@/composables/user";
|
import {useUserUpdating} from "~/composables/user/index.js";
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n();
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const { required, isEmail, isPasswordValid } = useValidators()
|
const { required, isEmail, isPasswordValid } = useValidators();
|
||||||
|
|
||||||
const userFirstName = computed(() => userStore.user?.firstName)
|
const user = computed(() => userStore.user);
|
||||||
const userLastName = computed(() => userStore.user?.lastName)
|
|
||||||
const userEmail = computed(() => userStore.user?.email)
|
|
||||||
const userPhoneNumber = computed(() => userStore.user?.phoneNumber)
|
|
||||||
|
|
||||||
const firstName = ref('')
|
const firstName = ref<string>('');
|
||||||
const lastName = ref('')
|
const lastName = ref<string>('');
|
||||||
const email = ref('')
|
const email = ref<string>('');
|
||||||
const phoneNumber = ref('')
|
const phoneNumber = ref<string>('');
|
||||||
const password = ref('')
|
const password = ref<string>('');
|
||||||
const confirmPassword = ref('')
|
const confirmPassword = ref<string>('');
|
||||||
|
|
||||||
const compareStrings = (v) => {
|
const compareStrings = (v: string) => {
|
||||||
if (v === password.value) return true
|
if (v === password.value) return true;
|
||||||
return t('errors.compare')
|
return t('errors.compare');
|
||||||
|
};
|
||||||
|
|
||||||
|
const { updateUser, loading } = useUserUpdating();
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
firstName.value = user.value?.firstName || '';
|
||||||
|
lastName.value = user.value?.lastName || '';
|
||||||
|
email.value = user.value?.email || '';
|
||||||
|
phoneNumber.value = user.value?.phoneNumber || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleUpdate() {
|
||||||
|
await updateUser(
|
||||||
|
firstName.value,
|
||||||
|
lastName.value,
|
||||||
|
email.value,
|
||||||
|
phoneNumber.value,
|
||||||
|
password.value,
|
||||||
|
confirmPassword.value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const { updateUser, loading } = useUserUpdating();
|
|
||||||
//
|
|
||||||
// watchEffect(() => {
|
|
||||||
// firstName.value = userFirstName.value || ''
|
|
||||||
// lastName.value = userLastName.value || ''
|
|
||||||
// email.value = userEmail.value || ''
|
|
||||||
// phoneNumber.value = userPhoneNumber.value || ''
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// async function handleUpdate() {
|
|
||||||
// await updateUser(
|
|
||||||
// firstName.value,
|
|
||||||
// lastName.value,
|
|
||||||
// email.value,
|
|
||||||
// phoneNumber.value,
|
|
||||||
// password.value,
|
|
||||||
// confirmPassword.value,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
@ -103,5 +100,16 @@ const compareStrings = (v) => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
|
&__box {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
width: fit-content;
|
||||||
|
padding-inline: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -1,62 +1,91 @@
|
||||||
<template>
|
<template>
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<nuxt-link-locale
|
<div class="nav__inner">
|
||||||
class="nav__item"
|
<nuxt-link-locale
|
||||||
:class="[{ active: route.path.includes('settings') }]"
|
class="nav__item"
|
||||||
to="/profile/settings"
|
:class="[{ active: route.path.includes('settings') }]"
|
||||||
>
|
to="/profile/settings"
|
||||||
<icon name="ic:baseline-settings" size="20" />
|
>
|
||||||
{{ t('profile.settings.title') }}
|
<icon name="ic:baseline-settings" size="20" />
|
||||||
</nuxt-link-locale>
|
{{ t('profile.settings.title') }}
|
||||||
<nuxt-link-locale
|
</nuxt-link-locale>
|
||||||
class="nav__item"
|
<nuxt-link-locale
|
||||||
:class="[{ active: route.path.includes('orders') }]"
|
class="nav__item"
|
||||||
to="/profile/orders"
|
:class="[{ active: route.path.includes('orders') }]"
|
||||||
>
|
to="/profile/orders"
|
||||||
<icon name="material-symbols:order-approve-rounded" size="20" />
|
>
|
||||||
{{ t('profile.orders.title') }}
|
<icon name="material-symbols:order-approve-rounded" size="20" />
|
||||||
</nuxt-link-locale>
|
{{ t('profile.orders.title') }}
|
||||||
<nuxt-link-locale
|
</nuxt-link-locale>
|
||||||
class="nav__item"
|
<nuxt-link-locale
|
||||||
:class="[{ active: route.path.includes('wishlist') }]"
|
class="nav__item"
|
||||||
to="/profile/wishlist"
|
:class="[{ active: route.path.includes('wishlist') }]"
|
||||||
>
|
to="/profile/wishlist"
|
||||||
<icon name="mdi:cards-heart-outline" size="20" />
|
>
|
||||||
{{ t('profile.wishlist.title') }}
|
<icon name="mdi:cards-heart-outline" size="20" />
|
||||||
</nuxt-link-locale>
|
{{ t('profile.wishlist.title') }}
|
||||||
<nuxt-link-locale
|
</nuxt-link-locale>
|
||||||
class="nav__item"
|
<nuxt-link-locale
|
||||||
:class="[{ active: route.path.includes('cart') }]"
|
class="nav__item"
|
||||||
to="/profile/cart"
|
:class="[{ active: route.path.includes('cart') }]"
|
||||||
>
|
to="/profile/cart"
|
||||||
<icon name="ph:shopping-cart-light" size="20" />
|
>
|
||||||
{{ t('profile.cart.title') }}
|
<icon name="ph:shopping-cart-light" size="20" />
|
||||||
</nuxt-link-locale>
|
{{ t('profile.cart.title') }}
|
||||||
|
</nuxt-link-locale>
|
||||||
|
<nuxt-link-locale
|
||||||
|
class="nav__item"
|
||||||
|
:class="[{ active: route.path.includes('balance') }]"
|
||||||
|
to="/profile/balance"
|
||||||
|
>
|
||||||
|
<icon name="ic:outline-attach-money" size="20" />
|
||||||
|
{{ t('profile.balance.title') }}
|
||||||
|
</nuxt-link-locale>
|
||||||
|
<nuxt-link-locale
|
||||||
|
class="nav__item"
|
||||||
|
:class="[{ active: route.path.includes('promocodes') }]"
|
||||||
|
to="/profile/promocodes"
|
||||||
|
>
|
||||||
|
<icon name="fluent:ticket-20-filled" size="20" />
|
||||||
|
{{ t('profile.promocodes.title') }}
|
||||||
|
</nuxt-link-locale>
|
||||||
|
</div>
|
||||||
|
<div class="nav__logout" @click="logout">
|
||||||
|
<icon name="material-symbols:power-settings-new-outline" size="20" />
|
||||||
|
{{ t('profile.logout') }}
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {useLogout} from "~/composables/auth";
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
|
const { logout } = useLogout();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.nav {
|
.nav {
|
||||||
background-color: $white;
|
|
||||||
border-radius: $default_border_radius;
|
|
||||||
padding-block: 15px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 141px;
|
top: 141px;
|
||||||
width: max-content;
|
width: max-content;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
background-color: $white;
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
padding-block: 7px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 5px 30px 5px 20px;
|
padding: 7px 30px 7px 10px;
|
||||||
border-left: 2px solid $white;
|
border-left: 2px solid $white;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -64,6 +93,7 @@ const route = useRoute();
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
|
|
||||||
color: $accent;
|
color: $accent;
|
||||||
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
||||||
@include hover {
|
@include hover {
|
||||||
|
|
@ -73,6 +103,29 @@ const route = useRoute();
|
||||||
&.active {
|
&.active {
|
||||||
border-color: $accent;
|
border-color: $accent;
|
||||||
color: $accentDark;
|
color: $accentDark;
|
||||||
|
background-color: rgba($accent, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__logout {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 25px;
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
background-color: rgba($accent, 0.2);
|
||||||
|
border: 1px solid $accent;
|
||||||
|
padding: 7px 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
color: $accent;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
@include hover {
|
||||||
|
background-color: $accent;
|
||||||
|
color: $white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@keydown="numberOnly ? onlyNumbersKeydown($event) : null"
|
@keydown="numberOnly ? onlyNumbersKeydown($event) : null"
|
||||||
class="block__input"
|
class="block__input"
|
||||||
|
:inputmode="inputMode || 'text'"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@click.prevent="togglePasswordVisible"
|
@click.prevent="togglePasswordVisible"
|
||||||
|
|
@ -31,9 +32,10 @@ const emit = defineEmits<{
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
type: string,
|
type: string,
|
||||||
placeholder: string,
|
placeholder: string,
|
||||||
modelValue?: [string, number],
|
modelValue?: string | number,
|
||||||
rules?: Rule[],
|
rules?: Rule[],
|
||||||
numberOnly?: boolean
|
numberOnly?: boolean,
|
||||||
|
inputMode?: "text" | "email" | "search" | "tel" | "url" | "none" | "numeric" | "decimal"
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const isPasswordVisible = ref(props.type);
|
const isPasswordVisible = ref(props.type);
|
||||||
|
|
@ -108,7 +110,7 @@ function onInput(e: Event) {
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: #2B2B2B;
|
color: #575757;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,9 @@
|
||||||
:src="currentLocale.flag"
|
:src="currentLocale.flag"
|
||||||
:alt="currentLocale.code"
|
:alt="currentLocale.code"
|
||||||
/>
|
/>
|
||||||
<skeletons-ui-language-switcher v-else />
|
<!-- <skeletons-ui-language-switcher v-else />-->
|
||||||
<template #fallback>
|
<template #fallback>
|
||||||
<skeletons-ui-language-switcher />
|
<!-- <skeletons-ui-language-switcher />-->
|
||||||
</template>
|
</template>
|
||||||
</client-only>
|
</client-only>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
:key="locale.code"
|
:key="locale.code"
|
||||||
format="webp"
|
format="webp"
|
||||||
densities="x1"
|
densities="x1"
|
||||||
@click="switchLanguage(locale.code)"
|
@click="uiSwitchLanguage(locale.code)"
|
||||||
:src="locale.flag"
|
:src="locale.flag"
|
||||||
:alt="locale.code"
|
:alt="locale.code"
|
||||||
/>
|
/>
|
||||||
|
|
@ -45,20 +45,25 @@
|
||||||
import {onClickOutside} from "@vueuse/core";
|
import {onClickOutside} from "@vueuse/core";
|
||||||
import {useLanguageSwitch} from "@/composables/languages/index.js";
|
import {useLanguageSwitch} from "@/composables/languages/index.js";
|
||||||
|
|
||||||
const languageStore = useLanguageStore()
|
const languageStore = useLanguageStore();
|
||||||
|
|
||||||
const locales = computed(() => languageStore.languages)
|
const locales = computed(() => languageStore.languages);
|
||||||
const currentLocale = computed(() => languageStore.currentLocale)
|
const currentLocale = computed(() => languageStore.currentLocale);
|
||||||
|
|
||||||
const isSwitcherVisible = ref<boolean>(false)
|
const isSwitcherVisible = ref<boolean>(false);
|
||||||
const setSwitcherVisible = (state) => {
|
const setSwitcherVisible = (state: boolean) => {
|
||||||
isSwitcherVisible.value = state
|
isSwitcherVisible.value = state;
|
||||||
}
|
};
|
||||||
|
|
||||||
const switcherRef = ref(null)
|
const switcherRef = ref(null);
|
||||||
onClickOutside(switcherRef, () => isSwitcherVisible.value = false)
|
onClickOutside(switcherRef, () => isSwitcherVisible.value = false);
|
||||||
|
|
||||||
const { switchLanguage } = useLanguageSwitch()
|
const { switchLanguage } = useLanguageSwitch();
|
||||||
|
|
||||||
|
const uiSwitchLanguage = (localeCode: string) => {
|
||||||
|
switchLanguage(localeCode);
|
||||||
|
setSwitcherVisible(false);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,19 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
routePath: string
|
routePath: string
|
||||||
}>()
|
}>();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const redirect = () => {
|
const redirect = () => {
|
||||||
if (props.routePath) {
|
if (props.routePath) {
|
||||||
router.push({
|
router.push({
|
||||||
path: props.routePath
|
path: props.routePath
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { useUserStore } from '~/stores/user';
|
||||||
import { useAppStore } from '~/stores/app';
|
import { useAppStore } from '~/stores/app';
|
||||||
import {DEFAULT_LOCALE} from "~/config/constants";
|
import {DEFAULT_LOCALE} from "~/config/constants";
|
||||||
import {useNotification} from "~/composables/notification";
|
import {useNotification} from "~/composables/notification";
|
||||||
|
import {usePromocodes} from "~/composables/promocodes";
|
||||||
|
|
||||||
export function useLogin() {
|
export function useLogin() {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -56,12 +57,14 @@ export function useLogin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
userStore.setUser(authData.user);
|
userStore.setUser(authData.user);
|
||||||
cookieAccess.value = authData.accessToken
|
cookieAccess.value = authData.accessToken;
|
||||||
|
|
||||||
useNotification(
|
appStore.unsetActiveState();
|
||||||
t('popup.success.login'),
|
|
||||||
'success'
|
useNotification({
|
||||||
);
|
message: t('popup.success.login'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
if (authData.user.language !== cookieLocale.value) {
|
if (authData.user.language !== cookieLocale.value) {
|
||||||
await checkAndRedirect(authData.user.language);
|
await checkAndRedirect(authData.user.language);
|
||||||
|
|
@ -69,8 +72,8 @@ export function useLogin() {
|
||||||
|
|
||||||
await useWishlist();
|
await useWishlist();
|
||||||
await usePendingOrder(authData.user.email);
|
await usePendingOrder(authData.user.email);
|
||||||
|
await usePromocodes();
|
||||||
appStore.unsetActiveState();
|
//TODO: combine three requests
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(error, (err) => {
|
watch(error, (err) => {
|
||||||
|
|
@ -82,11 +85,11 @@ export function useLogin() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ export function useNewPassword() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result?.data?.confirmResetPassword.success) {
|
if (result?.data?.confirmResetPassword.success) {
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.newPassword'),
|
message: t('popup.success.newPassword'),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
|
|
||||||
await router.push({path: '/'})
|
await router.push({path: '/'})
|
||||||
|
|
||||||
|
|
@ -46,11 +46,11 @@ export function useNewPassword() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@ export function usePasswordReset() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result?.data?.resetPassword.success) {
|
if (result?.data?.resetPassword.success) {
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.reset'),
|
message: t('popup.success.reset'),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
|
|
||||||
appStore.unsetActiveState();
|
appStore.unsetActiveState();
|
||||||
}
|
}
|
||||||
|
|
@ -35,11 +35,11 @@ export function usePasswordReset() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { useUserStore } from '~/stores/user';
|
||||||
import { isGraphQLError } from '~/utils/error';
|
import { isGraphQLError } from '~/utils/error';
|
||||||
import {DEFAULT_LOCALE} from "~/config/constants";
|
import {DEFAULT_LOCALE} from "~/config/constants";
|
||||||
import {useNotification} from "~/composables/notification";
|
import {useNotification} from "~/composables/notification";
|
||||||
|
import {usePromocodes} from "~/composables/promocodes";
|
||||||
|
|
||||||
export function useRefresh() {
|
export function useRefresh() {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -52,6 +53,8 @@ export function useRefresh() {
|
||||||
|
|
||||||
await useWishlist();
|
await useWishlist();
|
||||||
await usePendingOrder(data.user.email);
|
await usePendingOrder(data.user.email);
|
||||||
|
await usePromocodes();
|
||||||
|
//TODO: combine three requests
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(error, (err) => {
|
watch(error, (err) => {
|
||||||
|
|
@ -63,11 +66,11 @@ export function useRefresh() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,16 @@ import {isGraphQLError} from "~/utils/error";
|
||||||
import type {IRegisterResponse} from "~/types";
|
import type {IRegisterResponse} from "~/types";
|
||||||
import {useNotification} from "~/composables/notification";
|
import {useNotification} from "~/composables/notification";
|
||||||
|
|
||||||
|
interface IRegisterArguments {
|
||||||
|
firstName: string,
|
||||||
|
lastName: string,
|
||||||
|
phoneNumber: string,
|
||||||
|
email: string,
|
||||||
|
password: string,
|
||||||
|
confirmPassword: string,
|
||||||
|
referrer: string
|
||||||
|
}
|
||||||
|
|
||||||
export function useRegister() {
|
export function useRegister() {
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
@ -13,41 +23,37 @@ export function useRegister() {
|
||||||
const { mutate, loading, error } = useMutation<IRegisterResponse>(REGISTER);
|
const { mutate, loading, error } = useMutation<IRegisterResponse>(REGISTER);
|
||||||
|
|
||||||
async function register(
|
async function register(
|
||||||
firstName: string,
|
payload: IRegisterArguments
|
||||||
lastName: string,
|
|
||||||
phoneNumber: string,
|
|
||||||
email: string,
|
|
||||||
password: string,
|
|
||||||
confirmPassword: string
|
|
||||||
) {
|
) {
|
||||||
const result = await mutate({
|
const result = await mutate({
|
||||||
firstName,
|
firstName: payload.firstName,
|
||||||
lastName,
|
lastName: payload.lastName,
|
||||||
phoneNumber,
|
phoneNumber: payload.phoneNumber,
|
||||||
email,
|
email: payload.email,
|
||||||
password,
|
password: payload.password,
|
||||||
confirmPassword
|
confirmPassword: payload.confirmPassword,
|
||||||
|
referrer: payload.referrer
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result?.data?.createUser?.success) {
|
if (result?.data?.createUser?.success) {
|
||||||
detectMailClient(email);
|
detectMailClient(payload.email);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
h('div', [
|
message: h('div', [
|
||||||
h('p', t('popup.success.register')),
|
h('p', t('popup.success.register')),
|
||||||
mailClientUrl.value ? h(
|
mailClientUrl.value ? h(
|
||||||
'button',
|
'button',
|
||||||
{
|
{
|
||||||
class: 'el-notification__button',
|
class: 'el-notification__button',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
openMailClient()
|
openMailClient();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
t('buttons.goEmail')
|
t('buttons.goEmail')
|
||||||
) : ''
|
) : ''
|
||||||
]),
|
]),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
|
|
||||||
appStore.unsetActiveState();
|
appStore.unsetActiveState();
|
||||||
}
|
}
|
||||||
|
|
@ -62,11 +68,11 @@ export function useRegister() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
export const useAppConfig = () => {
|
export const useAppConfig = () => {
|
||||||
const runtimeConfig = useRuntimeConfig();
|
const runtimeConfig = useRuntimeConfig();
|
||||||
|
|
||||||
|
const APP_DOMAIN: string = runtimeConfig.public.evibesBaseDomain;
|
||||||
const APP_NAME: string = runtimeConfig.public.evibesProjectName;
|
const APP_NAME: string = runtimeConfig.public.evibesProjectName;
|
||||||
const APP_NAME_KEY: string = APP_NAME.toLowerCase();
|
const APP_NAME_KEY: string = APP_NAME.toLowerCase();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
APP_DOMAIN,
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
APP_NAME_KEY,
|
APP_NAME_KEY,
|
||||||
COOKIES_LOCALE_KEY: `${APP_NAME_KEY}-locale`,
|
COOKIES_LOCALE_KEY: `${APP_NAME_KEY}-locale`,
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ export function useContactUs() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result?.data?.contactUs.received) {
|
if (result?.data?.contactUs.received) {
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.contactUs'),
|
message: t('popup.success.contactUs'),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,11 +40,11 @@ export function useContactUs() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
1
storefront/composables/date/index.ts
Normal file
1
storefront/composables/date/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useDate';
|
||||||
14
storefront/composables/date/useDate.ts
Normal file
14
storefront/composables/date/useDate.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
export function useDate(
|
||||||
|
iso: string | undefined,
|
||||||
|
locale: string = 'en-gb'
|
||||||
|
): string {
|
||||||
|
if (!iso) return '';
|
||||||
|
const date = new Date(iso);
|
||||||
|
const parsedLocale = locale.replace('-', '-').toLocaleUpperCase()
|
||||||
|
|
||||||
|
return new Intl.DateTimeFormat(parsedLocale, {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: '2-digit'
|
||||||
|
}).format(date);
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,32 @@
|
||||||
export function useNotification(
|
import type {VNodeChild} from "vue";
|
||||||
message: string,
|
|
||||||
type: string,
|
|
||||||
title?: string
|
|
||||||
) {
|
|
||||||
const duration = 5000;
|
|
||||||
|
|
||||||
const createProgressBar = (duration: number, message: string) => {
|
interface INotificationArguments {
|
||||||
return h('div', [
|
message: string | VNodeChild,
|
||||||
h('p', message),
|
type: string,
|
||||||
h('div', {
|
title?: string
|
||||||
class: 'el-notification__progress',
|
}
|
||||||
style: {
|
|
||||||
animationDuration: `${duration}ms`
|
export function useNotification(
|
||||||
}
|
args: INotificationArguments
|
||||||
})
|
) {
|
||||||
]);
|
const duration = 5000
|
||||||
};
|
|
||||||
|
const progressBar = h('div', {
|
||||||
|
class: 'el-notification__progress',
|
||||||
|
style: { animationDuration: `${duration}ms` }
|
||||||
|
})
|
||||||
|
|
||||||
|
const bodyContent: VNodeChild =
|
||||||
|
typeof args.message === 'string'
|
||||||
|
? h('p', args.message)
|
||||||
|
: args.message
|
||||||
|
|
||||||
|
const messageVNode = h('div', [bodyContent, progressBar])
|
||||||
|
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: title,
|
title: args.title,
|
||||||
duration,
|
duration,
|
||||||
message: createProgressBar(duration, message),
|
message: messageVNode,
|
||||||
type: type
|
type: args.type
|
||||||
} as import('element-plus').NotificationOptions);
|
} as NotificationOptions)
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +26,9 @@ interface IOverwriteOrderArguments {
|
||||||
export function useOrderOverwrite () {
|
export function useOrderOverwrite () {
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
||||||
const orderUuid = computed(() => cartStore.currentOrder?.uuid);
|
const orderUuid = computed(() => cartStore.currentOrder?.uuid);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -58,94 +60,101 @@ export function useOrderOverwrite () {
|
||||||
async function overwriteOrder (
|
async function overwriteOrder (
|
||||||
args: IOverwriteOrderArguments
|
args: IOverwriteOrderArguments
|
||||||
) {
|
) {
|
||||||
switch (args.type) {
|
if (isAuthenticated.value) {
|
||||||
case "add":
|
switch (args.type) {
|
||||||
const addResult = await addMutate({
|
case "add":
|
||||||
orderUuid: orderUuid.value,
|
const addResult = await addMutate({
|
||||||
productUuid: args.productUuid
|
orderUuid: orderUuid.value,
|
||||||
});
|
productUuid: args.productUuid
|
||||||
|
});
|
||||||
|
|
||||||
if (addResult?.data?.addOrderProduct?.order) {
|
if (addResult?.data?.addOrderProduct?.order) {
|
||||||
cartStore.setCurrentOrders(addResult.data.addOrderProduct.order);
|
cartStore.setCurrentOrders(addResult.data.addOrderProduct.order);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.addToCart', { product: args.productName }),
|
message: t('popup.success.addToCart', { product: args.productName }),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "remove":
|
case "remove":
|
||||||
const removeResult = await removeMutate({
|
const removeResult = await removeMutate({
|
||||||
orderUuid: orderUuid.value,
|
orderUuid: orderUuid.value,
|
||||||
productUuid: args.productUuid
|
productUuid: args.productUuid
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeResult?.data?.removeOrderProduct?.order) {
|
if (removeResult?.data?.removeOrderProduct?.order) {
|
||||||
cartStore.setCurrentOrders(removeResult.data.removeOrderProduct.order);
|
cartStore.setCurrentOrders(removeResult.data.removeOrderProduct.order);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.removeFromCart', { product: args.productName }),
|
message: t('popup.success.removeFromCart', { product: args.productName }),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "removeKind":
|
case "removeKind":
|
||||||
const removeKindResult = await removeKindMutate({
|
const removeKindResult = await removeKindMutate({
|
||||||
orderUuid: orderUuid.value,
|
orderUuid: orderUuid.value,
|
||||||
productUuid: args.productUuid
|
productUuid: args.productUuid
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeKindResult?.data?.removeOrderProductsOfAKind?.order) {
|
if (removeKindResult?.data?.removeOrderProductsOfAKind?.order) {
|
||||||
cartStore.setCurrentOrders(removeKindResult.data.removeOrderProductsOfAKind.order);
|
cartStore.setCurrentOrders(removeKindResult.data.removeOrderProductsOfAKind.order);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.removeFromCart', { product: args.productName }),
|
message: t('popup.success.removeFromCart', { product: args.productName }),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "removeAll":
|
case "removeAll":
|
||||||
const removeAllResult = await removeAllMutate({
|
const removeAllResult = await removeAllMutate({
|
||||||
orderUuid: orderUuid.value,
|
orderUuid: orderUuid.value,
|
||||||
productUuid: args.productUuid
|
productUuid: args.productUuid
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeAllResult?.data?.removeAllOrderProducts?.order) {
|
if (removeAllResult?.data?.removeAllOrderProducts?.order) {
|
||||||
cartStore.setCurrentOrders(removeAllResult.data.removeAllOrderProducts.order);
|
cartStore.setCurrentOrders(removeAllResult.data.removeAllOrderProducts.order);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.removeAllFromCart', { product: args.productName }),
|
message: t('popup.success.removeAllFromCart', { product: args.productName }),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "bulk":
|
case "bulk":
|
||||||
const bulkResult = await bulkMutate({
|
const bulkResult = await bulkMutate({
|
||||||
orderUuid: orderUuid.value,
|
orderUuid: orderUuid.value,
|
||||||
action: args.bulkAction,
|
action: args.bulkAction,
|
||||||
products: args.products
|
products: args.products
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bulkResult?.data?.bulkOrderAction?.order) {
|
if (bulkResult?.data?.bulkOrderAction?.order) {
|
||||||
cartStore.setCurrentOrders(bulkResult.data.bulkOrderAction.order);
|
cartStore.setCurrentOrders(bulkResult.data.bulkOrderAction.order);
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.bulkRemoveWishlist'),
|
message: t('popup.success.bulkRemoveWishlist'),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.error('No type provided for overwriteOrder');
|
console.error('No type provided for overwriteOrder');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.errors.loginFirst'),
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,11 +167,11 @@ export function useOrderOverwrite () {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return{
|
return{
|
||||||
|
|
|
||||||
1
storefront/composables/promocodes/index.ts
Normal file
1
storefront/composables/promocodes/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './usePromocodes'
|
||||||
24
storefront/composables/promocodes/usePromocodes.ts
Normal file
24
storefront/composables/promocodes/usePromocodes.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import type {IPromocodesResponse} from "~/types";
|
||||||
|
import {GET_PROMOCODES} from "~/graphql/queries/standalone/promocodes";
|
||||||
|
|
||||||
|
export async function usePromocodes () {
|
||||||
|
const promocodesStore = usePromocodeStore();
|
||||||
|
|
||||||
|
const { data, error } = await useAsyncQuery<IPromocodesResponse>(
|
||||||
|
GET_PROMOCODES
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!error.value && data.value?.promocodes.edges) {
|
||||||
|
promocodesStore.setPromocodes(data.value.promocodes.edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(error, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('usePromocodes error:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -38,11 +38,11 @@ export function useSearch() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1 +1,4 @@
|
||||||
export * from './useUserActivation';
|
export * from './useUserActivation';
|
||||||
|
export * from './useAvatarUpload';
|
||||||
|
export * from './useUserUpdating';
|
||||||
|
export * from './useDeposit';
|
||||||
52
storefront/composables/user/useAvatarUpload.ts
Normal file
52
storefront/composables/user/useAvatarUpload.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import type {IAvatarUploadResponse,} from "~/types";
|
||||||
|
import {UPLOAD_AVATAR} from "~/graphql/mutations/user";
|
||||||
|
import {isGraphQLError} from "~/utils/error";
|
||||||
|
import {useNotification} from "~/composables/notification";
|
||||||
|
|
||||||
|
export function useAvatarUpload() {
|
||||||
|
const { t } = useI18n();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { mutate, onDone, loading, error } = useMutation<IAvatarUploadResponse>(UPLOAD_AVATAR, {
|
||||||
|
context: {
|
||||||
|
hasUpload: true
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
|
||||||
|
async function uploadAvatar(event: Event) {
|
||||||
|
const file = (event.target as HTMLInputElement).files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
await mutate({ avatar: file });
|
||||||
|
}
|
||||||
|
|
||||||
|
onDone(({ data }) => {
|
||||||
|
const user = data?.uploadAvatar.user;
|
||||||
|
if (user) {
|
||||||
|
userStore.setUser(user);
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.success.avatarUpload'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(error, (err) => {
|
||||||
|
if (!err) return;
|
||||||
|
console.error('useAvatarUpload error:', err);
|
||||||
|
let message = t('popup.errors.defaultError');
|
||||||
|
if (isGraphQLError(err)) {
|
||||||
|
message = err.graphQLErrors?.[0]?.message || message;
|
||||||
|
} else {
|
||||||
|
message = err.message;
|
||||||
|
}
|
||||||
|
useNotification({
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
title: t('popup.errors.main')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
uploadAvatar
|
||||||
|
};
|
||||||
|
}
|
||||||
42
storefront/composables/user/useDeposit.ts
Normal file
42
storefront/composables/user/useDeposit.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import {DEPOSIT} from "~/graphql/mutations/deposit";
|
||||||
|
import {isGraphQLError} from "~/utils/error";
|
||||||
|
import {useNotification} from "~/composables/notification";
|
||||||
|
|
||||||
|
export function useDeposit() {
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
const { mutate, loading, error } = useMutation(DEPOSIT);
|
||||||
|
|
||||||
|
async function deposit(
|
||||||
|
amount: string
|
||||||
|
) {
|
||||||
|
const result = await mutate(
|
||||||
|
{ amount }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result?.data?.deposit) {
|
||||||
|
window.open(result?.data.deposit.transaction.process.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(error, (err) => {
|
||||||
|
if (!err) return;
|
||||||
|
console.error('useDeposit error:', err);
|
||||||
|
let message = t('popup.errors.defaultError');
|
||||||
|
if (isGraphQLError(err)) {
|
||||||
|
message = err.graphQLErrors?.[0]?.message || message;
|
||||||
|
} else {
|
||||||
|
message = err.message;
|
||||||
|
}
|
||||||
|
useNotification({
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
title: t('popup.errors.main')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
deposit,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -15,10 +15,10 @@ export function useUserActivation() {
|
||||||
const result = await mutate({ token, uid });
|
const result = await mutate({ token, uid });
|
||||||
|
|
||||||
if (result?.data?.activateUser) {
|
if (result?.data?.activateUser) {
|
||||||
useNotification(
|
useNotification({
|
||||||
t("popup.activationSuccess"),
|
message: t("popup.activationSuccess"),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,11 +31,11 @@ export function useUserActivation() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
116
storefront/composables/user/useUserUpdating.ts
Normal file
116
storefront/composables/user/useUserUpdating.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
import {useLogout} from "@/composables/auth";
|
||||||
|
import {UPDATE_USER} from "~/graphql/mutations/user";
|
||||||
|
import type {IUserUpdatingResponse} from "~/types";
|
||||||
|
import {useAppConfig} from "~/composables/config";
|
||||||
|
import {isGraphQLError} from "~/utils/error";
|
||||||
|
import {useNotification} from "~/composables/notification";
|
||||||
|
import {DEFAULT_LOCALE} from "~/config/constants";
|
||||||
|
import {useLocaleRedirect} from "~/composables/languages";
|
||||||
|
|
||||||
|
export function useUserUpdating() {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
const { mutate, loading, error } = useMutation<IUserUpdatingResponse>(UPDATE_USER);
|
||||||
|
|
||||||
|
const { COOKIES_LOCALE_KEY } = useAppConfig();
|
||||||
|
const { checkAndRedirect } = useLocaleRedirect();
|
||||||
|
const { logout } = useLogout();
|
||||||
|
|
||||||
|
const cookieLocale = useCookie(
|
||||||
|
COOKIES_LOCALE_KEY,
|
||||||
|
{
|
||||||
|
default: () => DEFAULT_LOCALE,
|
||||||
|
path: '/'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const userUuid = computed(() => userStore.user?.uuid);
|
||||||
|
const userEmail = computed(() => userStore.user?.email);
|
||||||
|
|
||||||
|
async function updateUser(
|
||||||
|
firstName: string,
|
||||||
|
lastName: string,
|
||||||
|
email: string,
|
||||||
|
phoneNumber: string,
|
||||||
|
password: string,
|
||||||
|
confirmPassword: string
|
||||||
|
) {
|
||||||
|
const fields = {
|
||||||
|
uuid: userUuid.value,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
email,
|
||||||
|
phoneNumber,
|
||||||
|
password,
|
||||||
|
confirmPassword
|
||||||
|
};
|
||||||
|
|
||||||
|
const params = Object.fromEntries(
|
||||||
|
Object.entries(fields).filter(([_, value]) =>
|
||||||
|
value !== undefined && value !== null && value !== ''
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// if (('password' in params && !('passwordConfirm' in params)) ||
|
||||||
|
// (!('password' in params) && 'passwordConfirm' in params)) {
|
||||||
|
// ElNotification({
|
||||||
|
// title: t('popup.errors.main'),
|
||||||
|
// message: t('popup.errors.noDataToUpdate'),
|
||||||
|
// type: 'error'
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (Object.keys(params).length === 0) {
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.errors.noDataToUpdate'),
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await mutate(params);
|
||||||
|
const data = result?.data?.updateUser;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
if (userEmail.value !== email) {
|
||||||
|
await logout();
|
||||||
|
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.success.confirmEmail'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
userStore.setUser(data.user);
|
||||||
|
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.success.userUpdate'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.user.language !== cookieLocale.value) {
|
||||||
|
await checkAndRedirect(data.user.language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(error, (err) => {
|
||||||
|
if (!err) return;
|
||||||
|
console.error('useUserUpdating error:', err);
|
||||||
|
let message = t('popup.errors.defaultError');
|
||||||
|
if (isGraphQLError(err)) {
|
||||||
|
message = err.graphQLErrors?.[0]?.message || message;
|
||||||
|
} else {
|
||||||
|
message = err.message;
|
||||||
|
}
|
||||||
|
useNotification({
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
title: t('popup.errors.main')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateUser,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -9,12 +9,12 @@ export async function useWishlist() {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!error.value && data.value?.wishlists.edges[0]) {
|
if (!error.value && data.value?.wishlists.edges[0]) {
|
||||||
wishlistStore.setWishlist(data.value.wishlists.edges[0].node)
|
wishlistStore.setWishlist(data.value.wishlists.edges[0].node);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(error, (err) => {
|
watch(error, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('useWishlist error:', err)
|
console.error('useWishlist error:', err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ interface IOverwriteWishlistArguments {
|
||||||
export function useWishlistOverwrite() {
|
export function useWishlistOverwrite() {
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const wishlistStore = useWishlistStore();
|
const wishlistStore = useWishlistStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const isAuthenticated = computed(() => userStore.isAuthenticated);
|
||||||
const wishlistUuid = computed(() => wishlistStore.wishlist?.uuid);
|
const wishlistUuid = computed(() => wishlistStore.wishlist?.uuid);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|
@ -52,77 +54,84 @@ export function useWishlistOverwrite() {
|
||||||
async function overwriteWishlist (
|
async function overwriteWishlist (
|
||||||
args: IOverwriteWishlistArguments
|
args: IOverwriteWishlistArguments
|
||||||
) {
|
) {
|
||||||
switch (args.type) {
|
if (isAuthenticated.value) {
|
||||||
case "add":
|
switch (args.type) {
|
||||||
const addResult = await addMutate({
|
case "add":
|
||||||
wishlistUuid: wishlistUuid.value,
|
const addResult = await addMutate({
|
||||||
productUuid: args.productUuid
|
wishlistUuid: wishlistUuid.value,
|
||||||
});
|
productUuid: args.productUuid
|
||||||
|
});
|
||||||
|
|
||||||
if (addResult?.data?.addWishlistProduct?.wishlist) {
|
if (addResult?.data?.addWishlistProduct?.wishlist) {
|
||||||
wishlistStore.setWishlist(addResult.data.addWishlistProduct.wishlist);
|
wishlistStore.setWishlist(addResult.data.addWishlistProduct.wishlist);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.addToWishlist', { product: args.productName }),
|
message: t('popup.success.addToWishlist', { product: args.productName }),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "remove":
|
case "remove":
|
||||||
const removeResult = await removeMutate({
|
const removeResult = await removeMutate({
|
||||||
wishlistUuid: wishlistUuid.value,
|
wishlistUuid: wishlistUuid.value,
|
||||||
productUuid: args.productUuid
|
productUuid: args.productUuid
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeResult?.data?.removeWishlistProduct?.wishlist) {
|
if (removeResult?.data?.removeWishlistProduct?.wishlist) {
|
||||||
wishlistStore.setWishlist(removeResult.data.removeWishlistProduct.wishlist);
|
wishlistStore.setWishlist(removeResult.data.removeWishlistProduct.wishlist);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.removeFromWishlist', { product: args.productName }),
|
message: t('popup.success.removeFromWishlist', { product: args.productName }),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "removeAll":
|
case "removeAll":
|
||||||
const removeAllResult = await removeAllMutate({
|
const removeAllResult = await removeAllMutate({
|
||||||
wishlistUuid: wishlistUuid.value,
|
wishlistUuid: wishlistUuid.value,
|
||||||
productUuid: args.productUuid
|
productUuid: args.productUuid
|
||||||
});
|
});
|
||||||
|
|
||||||
if (removeAllResult?.data?.removeAllWishlistProducts?.wishlist) {
|
if (removeAllResult?.data?.removeAllWishlistProducts?.wishlist) {
|
||||||
wishlistStore.setWishlist(removeAllResult.data.removeAllWishlistProducts.wishlist);
|
wishlistStore.setWishlist(removeAllResult.data.removeAllWishlistProducts.wishlist);
|
||||||
|
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.removeAllFromWishlist'),
|
message: t('popup.success.removeAllFromWishlist'),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "bulk":
|
case "bulk":
|
||||||
const bulkResult = await bulkMutate({
|
const bulkResult = await bulkMutate({
|
||||||
wishlistUuid: wishlistUuid.value,
|
wishlistUuid: wishlistUuid.value,
|
||||||
action: args.bulkAction,
|
action: args.bulkAction,
|
||||||
products: args.products
|
products: args.products
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bulkResult?.data?.bulkWishlistAction?.wishlist) {
|
if (bulkResult?.data?.bulkWishlistAction?.wishlist) {
|
||||||
wishlistStore.setWishlist(bulkResult.data.bulkWishlistAction.wishlist);
|
wishlistStore.setWishlist(bulkResult.data.bulkWishlistAction.wishlist);
|
||||||
useNotification(
|
useNotification({
|
||||||
t('popup.success.bulkRemoveWishlist'),
|
message: t('popup.success.bulkRemoveWishlist'),
|
||||||
'success'
|
type: 'success'
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.error('No type provided for overwriteWishlist');
|
console.error('No type provided for overwriteWishlist');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.errors.loginFirst'),
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,11 +144,11 @@ export function useWishlistOverwrite() {
|
||||||
} else {
|
} else {
|
||||||
message = err.message;
|
message = err.message;
|
||||||
}
|
}
|
||||||
useNotification(
|
useNotification({
|
||||||
message,
|
message,
|
||||||
'error',
|
type: 'error',
|
||||||
t('popup.errors.main')
|
title: t('popup.errors.main')
|
||||||
);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return{
|
return{
|
||||||
|
|
|
||||||
|
|
@ -85,4 +85,6 @@ export const SUPPORTED_LOCALES: LocaleDefinition[] = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const DEFAULT_LOCALE = SUPPORTED_LOCALES.find(locale => locale.default)?.code || 'en-gb';
|
export const DEFAULT_LOCALE = SUPPORTED_LOCALES.find(locale => locale.default)?.code || 'en-gb';
|
||||||
|
|
||||||
|
export const CURRENCY = '$'
|
||||||
12
storefront/graphql/fragments/promocodes.fragment.ts
Normal file
12
storefront/graphql/fragments/promocodes.fragment.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
export const PROMOCODE_FRAGMENT = gql`
|
||||||
|
fragment Promocode on PromoCodeType {
|
||||||
|
code
|
||||||
|
discount
|
||||||
|
discountType
|
||||||
|
endTime
|
||||||
|
id
|
||||||
|
startTime
|
||||||
|
usedOn
|
||||||
|
uuid
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
@ -12,5 +12,10 @@ export const USER_FRAGMENT = gql`
|
||||||
balance {
|
balance {
|
||||||
amount
|
amount
|
||||||
}
|
}
|
||||||
|
orders {
|
||||||
|
uuid
|
||||||
|
humanReadableId
|
||||||
|
status
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
@ -7,7 +7,8 @@ export const REGISTER = gql`
|
||||||
$email: String!,
|
$email: String!,
|
||||||
$phoneNumber: String!,
|
$phoneNumber: String!,
|
||||||
$password: String!,
|
$password: String!,
|
||||||
$confirmPassword: String!
|
$confirmPassword: String!,
|
||||||
|
$referrer: String!,
|
||||||
) {
|
) {
|
||||||
createUser(
|
createUser(
|
||||||
firstName: $firstName,
|
firstName: $firstName,
|
||||||
|
|
@ -15,7 +16,8 @@ export const REGISTER = gql`
|
||||||
email: $email,
|
email: $email,
|
||||||
phoneNumber: $phoneNumber,
|
phoneNumber: $phoneNumber,
|
||||||
password: $password,
|
password: $password,
|
||||||
confirmPassword: $confirmPassword
|
confirmPassword: $confirmPassword,
|
||||||
|
referrer: $referrer
|
||||||
) {
|
) {
|
||||||
success
|
success
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,4 +39,19 @@ export const UPDATE_USER = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${USER_FRAGMENT}
|
${USER_FRAGMENT}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const UPLOAD_AVATAR = gql`
|
||||||
|
mutation uploadAvatar(
|
||||||
|
$avatar: Upload!
|
||||||
|
) {
|
||||||
|
uploadAvatar(
|
||||||
|
avatar: $avatar
|
||||||
|
) {
|
||||||
|
user {
|
||||||
|
...User
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${USER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import combineQuery from 'graphql-combine-query'
|
|
||||||
import {GET_PRODUCTS} from "~/graphql/queries/standalone/products";
|
|
||||||
import {GET_CATEGORY_BY_SLUG} from "~/graphql/queries/standalone/categories";
|
|
||||||
|
|
||||||
export const getStore = (
|
|
||||||
productsVariables?: {
|
|
||||||
first?: number;
|
|
||||||
productAfter?: string;
|
|
||||||
categoriesSlug?: string;
|
|
||||||
orderby?: string;
|
|
||||||
minPrice?: number;
|
|
||||||
maxPrice?: number;
|
|
||||||
attributes?: string;
|
|
||||||
},
|
|
||||||
categoryVariables?: {
|
|
||||||
categorySlug?: string;
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
const { document, variables } = combineQuery('getStoreData')
|
|
||||||
.add(GET_PRODUCTS, productsVariables || {})
|
|
||||||
.add(GET_CATEGORY_BY_SLUG, categoryVariables || {})
|
|
||||||
|
|
||||||
return { document, variables };
|
|
||||||
};
|
|
||||||
14
storefront/graphql/queries/standalone/promocodes.ts
Normal file
14
storefront/graphql/queries/standalone/promocodes.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import {PROMOCODE_FRAGMENT} from "~/graphql/fragments/promocodes.fragment";
|
||||||
|
|
||||||
|
export const GET_PROMOCODES = gql`
|
||||||
|
query getPromocodes {
|
||||||
|
promocodes {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...Promocode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${PROMOCODE_FRAGMENT}
|
||||||
|
`
|
||||||
|
|
@ -45,7 +45,8 @@
|
||||||
"errors": {
|
"errors": {
|
||||||
"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!"
|
||||||
},
|
},
|
||||||
"success": {
|
"success": {
|
||||||
"login": "Sign in successes",
|
"login": "Sign in successes",
|
||||||
|
|
@ -60,7 +61,11 @@
|
||||||
"addToWishlist": "{product} has been added to the wishlist!",
|
"addToWishlist": "{product} has been added to the wishlist!",
|
||||||
"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!",
|
||||||
|
"avatarUpload": "You have successfully uploaded an avatar!",
|
||||||
|
"userUpdate": "Profile successfully updated!",
|
||||||
|
"referralCopy": "You copied your referal link!",
|
||||||
|
"promocodeCopy": "You copied your promocode!"
|
||||||
},
|
},
|
||||||
"addToCartLimit": "Total quantity limit is {quantity}!",
|
"addToCartLimit": "Total quantity limit is {quantity}!",
|
||||||
"failAdd": "Please log in to make a purchase",
|
"failAdd": "Please log in to make a purchase",
|
||||||
|
|
@ -122,7 +127,10 @@
|
||||||
"catalog": "Catalog",
|
"catalog": "Catalog",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"wishlist": "Wishlist",
|
"wishlist": "Wishlist",
|
||||||
"cart": "Cart"
|
"cart": "Cart",
|
||||||
|
"settings": "Settings",
|
||||||
|
"balance": "Balance",
|
||||||
|
"promocodes": "Promocodes"
|
||||||
},
|
},
|
||||||
"contact": {
|
"contact": {
|
||||||
"title": "Contact us"
|
"title": "Contact us"
|
||||||
|
|
@ -148,19 +156,31 @@
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings"
|
"title": "Settings",
|
||||||
|
"joinData": "Date of registration",
|
||||||
|
"accountInfo": "Account info",
|
||||||
|
"copyReferral": "Copy my referral link",
|
||||||
|
"referralTooltip": "You will get a referral link after a successful purchase"
|
||||||
},
|
},
|
||||||
"orders": {
|
"orders": {
|
||||||
"title": "Orders"
|
"title": "Orders"
|
||||||
},
|
},
|
||||||
"wishlist": {
|
"wishlist": {
|
||||||
"title": "Wishlist",
|
"title": "Wishlist",
|
||||||
"total": "{quantity} items worth {amount}"
|
"total": "{quantity} items worth {amount}",
|
||||||
|
"deleteTooltip": "Delete all from wishlist"
|
||||||
},
|
},
|
||||||
"cart": {
|
"cart": {
|
||||||
"title": "Cart",
|
"title": "Cart",
|
||||||
"quantity": "Quantity: ",
|
"quantity": "Quantity: ",
|
||||||
"total": "Total: "
|
"total": "Total: "
|
||||||
}
|
},
|
||||||
|
"balance": {
|
||||||
|
"title": "Balance"
|
||||||
|
},
|
||||||
|
"promocodes": {
|
||||||
|
"title": "Promocodes"
|
||||||
|
},
|
||||||
|
"logout": "Logout"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -27,9 +27,9 @@ export default defineNuxtConfig({
|
||||||
authType: 'Bearer',
|
authType: 'Bearer',
|
||||||
authHeader: 'X-EVIBES-AUTH',
|
authHeader: 'X-EVIBES-AUTH',
|
||||||
tokenStorage: 'cookie',
|
tokenStorage: 'cookie',
|
||||||
tokenName: `${process.env.EVIBES_PROJECT_NAME?.toLowerCase()}-access`
|
tokenName: `${process.env.EVIBES_PROJECT_NAME?.toLowerCase()}-access`,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
|
|
|
||||||
51
storefront/package-lock.json
generated
51
storefront/package-lock.json
generated
|
|
@ -15,6 +15,7 @@
|
||||||
"@vueuse/integrations": "^13.3.0",
|
"@vueuse/integrations": "^13.3.0",
|
||||||
"@vueuse/nuxt": "^13.3.0",
|
"@vueuse/nuxt": "^13.3.0",
|
||||||
"@vueuse/router": "^13.3.0",
|
"@vueuse/router": "^13.3.0",
|
||||||
|
"apollo-upload-client": "17.0.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"graphql-combine-query": "^1.2.4",
|
"graphql-combine-query": "^1.2.4",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
|
|
@ -75,7 +76,6 @@
|
||||||
"version": "3.13.8",
|
"version": "3.13.8",
|
||||||
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.8.tgz",
|
"resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.8.tgz",
|
||||||
"integrity": "sha512-YM9lQpm0VfVco4DSyKooHS/fDTiKQcCHfxr7i3iL6a0kP/jNO5+4NFK6vtRDxaYisd5BrwOZHLJpPBnvRVpKPg==",
|
"integrity": "sha512-YM9lQpm0VfVco4DSyKooHS/fDTiKQcCHfxr7i3iL6a0kP/jNO5+4NFK6vtRDxaYisd5BrwOZHLJpPBnvRVpKPg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@graphql-typed-document-node/core": "^3.1.1",
|
"@graphql-typed-document-node/core": "^3.1.1",
|
||||||
|
|
@ -896,7 +896,6 @@
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
|
||||||
"integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
|
"integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
||||||
|
|
@ -3488,7 +3487,6 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
|
||||||
"integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
|
"integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
|
|
@ -3501,7 +3499,6 @@
|
||||||
"version": "0.7.4",
|
"version": "0.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
|
||||||
"integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
|
"integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
|
|
@ -3514,7 +3511,6 @@
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
"resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
|
"resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
|
||||||
"integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
|
"integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
|
|
@ -3527,7 +3523,6 @@
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
|
||||||
"integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
|
"integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.0"
|
||||||
|
|
@ -3678,6 +3673,25 @@
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/apollo-upload-client": {
|
||||||
|
"version": "17.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/apollo-upload-client/-/apollo-upload-client-17.0.0.tgz",
|
||||||
|
"integrity": "sha512-pue33bWVbdlXAGFPkgz53TTmxVMrKeQr0mdRcftNY+PoHIdbGZD0hoaXHvO6OePJAkFz7OiCFUf98p1G/9+Ykw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"extract-files": "^11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.22.0 || ^14.17.0 || >= 16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jaydenseric"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@apollo/client": "^3.0.0",
|
||||||
|
"graphql": "14 - 16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/archiver": {
|
"node_modules/archiver": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
|
||||||
|
|
@ -6208,6 +6222,18 @@
|
||||||
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
|
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/extract-files": {
|
||||||
|
"version": "11.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz",
|
||||||
|
"integrity": "sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20 || >= 14.13"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jaydenseric"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/extract-zip": {
|
"node_modules/extract-zip": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||||
|
|
@ -6888,7 +6914,7 @@
|
||||||
"version": "5.16.2",
|
"version": "5.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.2.tgz",
|
||||||
"integrity": "sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ==",
|
"integrity": "sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
|
|
@ -6998,7 +7024,6 @@
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react-is": "^16.7.0"
|
"react-is": "^16.7.0"
|
||||||
|
|
@ -8229,7 +8254,6 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
|
@ -9110,7 +9134,6 @@
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
|
|
@ -9250,7 +9273,6 @@
|
||||||
"version": "0.18.1",
|
"version": "0.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz",
|
||||||
"integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==",
|
"integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wry/caches": "^1.0.0",
|
"@wry/caches": "^1.0.0",
|
||||||
|
|
@ -10290,7 +10312,6 @@
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
|
|
@ -10458,7 +10479,6 @@
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/read-package-up": {
|
"node_modules/read-package-up": {
|
||||||
|
|
@ -10572,7 +10592,6 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
|
||||||
"integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
|
"integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
|
|
@ -11606,7 +11625,6 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
|
||||||
"integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
|
"integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
|
|
@ -11870,7 +11888,6 @@
|
||||||
"version": "0.10.3",
|
"version": "0.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
|
"resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
|
||||||
"integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
|
"integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
|
|
@ -13305,14 +13322,12 @@
|
||||||
"version": "0.8.15",
|
"version": "0.8.15",
|
||||||
"resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
|
"resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
|
||||||
"integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==",
|
"integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/zen-observable-ts": {
|
"node_modules/zen-observable-ts": {
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
|
||||||
"integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
|
"integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"zen-observable": "0.8.15"
|
"zen-observable": "0.8.15"
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
"@vueuse/integrations": "^13.3.0",
|
"@vueuse/integrations": "^13.3.0",
|
||||||
"@vueuse/nuxt": "^13.3.0",
|
"@vueuse/nuxt": "^13.3.0",
|
||||||
"@vueuse/router": "^13.3.0",
|
"@vueuse/router": "^13.3.0",
|
||||||
|
"apollo-upload-client": "17.0.0",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"graphql-combine-query": "^1.2.4",
|
"graphql-combine-query": "^1.2.4",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {useBrandByUuid} from "~/composables/brands";
|
import {useBrandByUuid} from "~/composables/brands";
|
||||||
import {usePageTitle} from "~/composables/utils/index.js";
|
import {usePageTitle} from "~/composables/utils";
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,16 +54,42 @@
|
||||||
allow-half
|
allow-half
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
<div class="product__info-price">{{ product.price }}</div>
|
<div class="product__info-price">{{ product.price }} {{ CURRENCY }}</div>
|
||||||
<div class="product__info-bottom">
|
<div class="product__info-bottom">
|
||||||
<ui-button
|
<ui-button
|
||||||
class="product__info-button"
|
class="product__info-button"
|
||||||
|
v-if="isProductInCart"
|
||||||
|
@click="overwriteOrder({
|
||||||
|
type: 'remove',
|
||||||
|
productUuid: product.uuid,
|
||||||
|
productName: product.name
|
||||||
|
})"
|
||||||
|
:isLoading="removeLoading"
|
||||||
|
>
|
||||||
|
{{ t('buttons.removeFromCart') }}
|
||||||
|
</ui-button>
|
||||||
|
<ui-button
|
||||||
|
class="product__info-button"
|
||||||
|
v-else
|
||||||
|
@click="overwriteOrder({
|
||||||
|
type: 'add',
|
||||||
|
productUuid: product.uuid,
|
||||||
|
productName: product.name
|
||||||
|
})"
|
||||||
|
:isLoading="addLoading"
|
||||||
>
|
>
|
||||||
{{ t('buttons.addToCart') }}
|
{{ t('buttons.addToCart') }}
|
||||||
</ui-button>
|
</ui-button>
|
||||||
<div class="product__info-wishlist">
|
<div
|
||||||
<icon name="mdi:cards-heart-outline" size="28" />
|
class="product__info-wishlist"
|
||||||
<!-- <icon name="mdi:cards-heart" size="28" />-->
|
@click="overwriteWishlist({
|
||||||
|
type: (isProductInWishlist ? 'remove' : 'add'),
|
||||||
|
productUuid: product.uuid,
|
||||||
|
productName: product.name
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
<icon name="mdi:cards-heart" size="28" v-if="isProductInWishlist" />
|
||||||
|
<icon name="mdi:cards-heart-outline" size="28" v-else />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -109,7 +135,7 @@
|
||||||
slidesPerView: 4
|
slidesPerView: 4
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
:navigation="{ prevEl: prevButton, nextEl: nextButton }"
|
:navigation="{ prevEl: '.prev', nextEl: '.next' }"
|
||||||
>
|
>
|
||||||
<swiper-slide
|
<swiper-slide
|
||||||
v-for="prod in products"
|
v-for="prod in products"
|
||||||
|
|
@ -138,20 +164,38 @@ import { Swiper, SwiperSlide } from 'swiper/vue';
|
||||||
import 'swiper/css';
|
import 'swiper/css';
|
||||||
import 'swiper/css/navigation';
|
import 'swiper/css/navigation';
|
||||||
import {Navigation} from "swiper/modules";
|
import {Navigation} from "swiper/modules";
|
||||||
|
import {CURRENCY} from "~/config/constants";
|
||||||
|
import {useWishlistOverwrite} from "~/composables/wishlist";
|
||||||
|
import {useOrderOverwrite} from "~/composables/orders/useOrderOverwrite";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
|
const wishlistStore = useWishlistStore();
|
||||||
|
const cartStore = useCartStore();
|
||||||
|
|
||||||
const { setPageTitle } = usePageTitle();
|
const { setPageTitle } = usePageTitle();
|
||||||
const { scrollTo } = useScrollTo();
|
const { scrollTo } = useScrollTo();
|
||||||
|
|
||||||
const slug = useRouteParams<string>('slug');
|
const slug = useRouteParams<string>('slug');
|
||||||
|
|
||||||
|
const { overwriteWishlist } = useWishlistOverwrite();
|
||||||
|
const { addLoading, removeLoading, overwriteOrder } = useOrderOverwrite();
|
||||||
const { product } = await useProductBySlug(slug.value);
|
const { product } = await useProductBySlug(slug.value);
|
||||||
const { products, getProducts } = await useProducts();
|
const { products, getProducts } = await useProducts();
|
||||||
await getProducts({
|
await getProducts({
|
||||||
categoriesSlugs: product.value?.category.slug
|
categoriesSlugs: product.value?.category.slug
|
||||||
})
|
});
|
||||||
|
|
||||||
|
const isProductInWishlist = computed(() => {
|
||||||
|
const el = wishlistStore.wishlist?.products?.edges.find(
|
||||||
|
(el) => el?.node?.uuid === product.value?.uuid
|
||||||
|
);
|
||||||
|
|
||||||
|
return !!el;
|
||||||
|
});
|
||||||
|
const isProductInCart = computed(() => {
|
||||||
|
return cartStore.currentOrder?.orderProducts?.edges.find((prod) => prod.node.product.uuid === product.value?.uuid);
|
||||||
|
});
|
||||||
|
|
||||||
const images = computed<string[]>(() =>
|
const images = computed<string[]>(() =>
|
||||||
product.value
|
product.value
|
||||||
|
|
|
||||||
33
storefront/pages/profile/balance.vue
Normal file
33
storefront/pages/profile/balance.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<div class="balance">
|
||||||
|
<h2>{{ t('profile.balance.title') }}: {{ user?.balance.amount }}</h2>
|
||||||
|
<forms-deposit />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {usePageTitle} from "~/composables/utils";
|
||||||
|
|
||||||
|
const {t} = useI18n();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const user = computed(() => userStore.user);
|
||||||
|
|
||||||
|
const { setPageTitle } = usePageTitle();
|
||||||
|
|
||||||
|
setPageTitle(t('breadcrumbs.balance'));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.balance {
|
||||||
|
background-color: $white;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
height: fit-content;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -13,13 +13,14 @@
|
||||||
:key="product.node.uuid"
|
:key="product.node.uuid"
|
||||||
:product="product.node.product"
|
:product="product.node.product"
|
||||||
:isList="true"
|
:isList="true"
|
||||||
|
:isToolsVisible="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import {usePageTitle} from "~/composables/utils/index.js";
|
import {usePageTitle} from "~/composables/utils";
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const cartStore = useCartStore();
|
const cartStore = useCartStore();
|
||||||
|
|
@ -61,6 +62,7 @@ setPageTitle(t('breadcrumbs.cart'));
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
|
||||||
& p {
|
& p {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
@ -80,6 +82,7 @@ setPageTitle(t('breadcrumbs.cart'));
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: $default_border_radius;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
88
storefront/pages/profile/promocodes.vue
Normal file
88
storefront/pages/profile/promocodes.vue
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<template>
|
||||||
|
<div class="promocodes">
|
||||||
|
<h2>{{ t('profile.promocodes.title') }}</h2>
|
||||||
|
<div class="promocodes__list">
|
||||||
|
<div
|
||||||
|
class="promocodes__item"
|
||||||
|
v-for="promocode in promocodes"
|
||||||
|
:key="promocode.node.uuid"
|
||||||
|
>
|
||||||
|
<icon
|
||||||
|
name="material-symbols:content-copy"
|
||||||
|
size="20"
|
||||||
|
class="promocodes__item-button"
|
||||||
|
@click="copyCode(promocode.node.code)"
|
||||||
|
/>
|
||||||
|
<p>{{ promocode.node.code }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {usePageTitle} from "~/composables/utils";
|
||||||
|
import {useNotification} from "~/composables/notification/index.js";
|
||||||
|
|
||||||
|
const {t} = useI18n();
|
||||||
|
const promocodesStore = usePromocodeStore();
|
||||||
|
|
||||||
|
const promocodes = computed(() => promocodesStore.promocodes);
|
||||||
|
|
||||||
|
const copyCode = (code: string) => {
|
||||||
|
navigator.clipboard.writeText(code)
|
||||||
|
.then(() => {
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.success.promocodeCopy'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// TODO: display more info about promo
|
||||||
|
const { setPageTitle } = usePageTitle();
|
||||||
|
|
||||||
|
setPageTitle(t('breadcrumbs.promocodes'));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.promocodes {
|
||||||
|
background-color: $white;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
height: fit-content;
|
||||||
|
|
||||||
|
&__list {
|
||||||
|
margin-top: 50px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
border: 1px solid $accent;
|
||||||
|
padding: 7px 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s;
|
||||||
|
color: $accentDark;
|
||||||
|
|
||||||
|
@include hover {
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,15 +1,239 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="settings">
|
<div class="settings">
|
||||||
<p>settings</p>
|
<div class="settings__top">
|
||||||
|
<div class="settings__top-left">
|
||||||
|
<div class="settings__avatar">
|
||||||
|
<input type="file" id="avatar" @change="uploadAvatar" />
|
||||||
|
<label for="avatar">
|
||||||
|
<img class="settings__avatar-image" v-if="user?.avatar" :src="user?.avatar" alt="">
|
||||||
|
<icon name="material-symbols-light:person-outline-rounded" size="100" />
|
||||||
|
<span class="settings__avatar-inner">
|
||||||
|
<icon name="material-symbols:upload" size="40" />
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="settings__top-inner">
|
||||||
|
<h2>{{ user?.firstName }} {{ user?.lastName }}</h2>
|
||||||
|
<p>{{ t('profile.settings.joinData') }}: {{ joinData }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-tooltip
|
||||||
|
:content="t('profile.settings.referralTooltip')"
|
||||||
|
placement="top-end"
|
||||||
|
:disabled="finishedOrdersQuantity > 0"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="settings__button"
|
||||||
|
@click="copyReferral"
|
||||||
|
:disabled="finishedOrdersQuantity === 0"
|
||||||
|
>
|
||||||
|
<icon name="material-symbols:content-copy" size="20" />
|
||||||
|
{{ t('profile.settings.copyReferral') }}
|
||||||
|
</button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="settings__main">
|
||||||
|
<p>{{ t('profile.settings.accountInfo') }}</p>
|
||||||
|
<forms-update />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {usePageTitle} from "~/composables/utils";
|
||||||
|
import { useDate } from '~/composables/date';
|
||||||
|
import {DEFAULT_LOCALE} from "~/config/constants";
|
||||||
|
import { useAppConfig } from "~/composables/config";
|
||||||
|
import {useAvatarUpload} from "~/composables/user";
|
||||||
|
import {useNotification} from "~/composables/notification";
|
||||||
|
|
||||||
|
const {t} = useI18n();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const { APP_DOMAIN, COOKIES_LOCALE_KEY } = useAppConfig();
|
||||||
|
const { uploadAvatar } = useAvatarUpload();
|
||||||
|
|
||||||
|
const cookieLocale = useCookie(
|
||||||
|
COOKIES_LOCALE_KEY,
|
||||||
|
{
|
||||||
|
default: () => DEFAULT_LOCALE,
|
||||||
|
path: '/'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const user = computed(() => userStore.user);
|
||||||
|
const finishedOrdersQuantity = computed(() => userStore.finishedOrdersQuantity);
|
||||||
|
const joinData = computed(() => {
|
||||||
|
return useDate(
|
||||||
|
user.value?.dateJoined, cookieLocale.value
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const referralLink = computed(() => {
|
||||||
|
if (finishedOrdersQuantity.value > 0) {
|
||||||
|
return `https://${APP_DOMAIN}/${DEFAULT_LOCALE}/?referrer=` + user.value?.uuid;
|
||||||
|
} else {
|
||||||
|
return `https://${APP_DOMAIN}/${DEFAULT_LOCALE}/`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const copyReferral = () => {
|
||||||
|
if (finishedOrdersQuantity.value > 0) {
|
||||||
|
navigator.clipboard.writeText(referralLink.value)
|
||||||
|
.then(() => {
|
||||||
|
useNotification({
|
||||||
|
message: t('popup.success.referralCopy'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { setPageTitle } = usePageTitle();
|
||||||
|
|
||||||
|
setPageTitle(t('breadcrumbs.settings'));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.settings {
|
.settings {
|
||||||
|
background-color: $white;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
height: fit-content;
|
||||||
|
|
||||||
|
&__top {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 25px;
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-inner {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
& h2 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__avatar {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
border: 2px solid $accent;
|
||||||
|
background-color: rgba($accent, 0.2);
|
||||||
|
box-shadow: 0 0 9.1px 0 rgba(0, 0, 0, 0.20);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
@include hover {
|
||||||
|
.settings__avatar-inner {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& label {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
& span {
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-inner {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
inset: 0;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.2s;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
|
||||||
|
& span {
|
||||||
|
font-size: 60px !important;
|
||||||
|
color: $white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
background-color: rgba($accent, 0.2);
|
||||||
|
border: 1px solid $accent;
|
||||||
|
padding: 5px 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
color: $accent;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
@include hover {
|
||||||
|
background-color: $accent;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #c0c0c0;
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
@include hover {
|
||||||
|
background-color: #c0c0c0;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__main {
|
||||||
|
margin-top: 50px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 25px;
|
||||||
|
|
||||||
|
& p {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $accentDark;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -11,12 +11,17 @@
|
||||||
</ui-checkbox>
|
</ui-checkbox>
|
||||||
<p>{{ t('profile.wishlist.total', {quantity: productsInWishlist.length, amount: totalPrice}) }}</p>
|
<p>{{ t('profile.wishlist.total', {quantity: productsInWishlist.length, amount: totalPrice}) }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<el-tooltip
|
||||||
class="wishlist__top-button"
|
:content="t('profile.wishlist.deleteTooltip')"
|
||||||
@click="onBulkRemove"
|
placement="top-end"
|
||||||
>
|
>
|
||||||
<icon name="material-symbols-light:delete-rounded" size="20" />
|
<div
|
||||||
</div>
|
class="wishlist__top-button"
|
||||||
|
@click="onBulkRemove"
|
||||||
|
>
|
||||||
|
<icon name="material-symbols-light:delete-rounded" size="20" />
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="wishlist__list">
|
<div class="wishlist__list">
|
||||||
<div class="wishlist__list-inner">
|
<div class="wishlist__list-inner">
|
||||||
|
|
@ -118,6 +123,7 @@ setPageTitle(t('breadcrumbs.wishlist'));
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: $default_border_radius;
|
||||||
|
|
||||||
&-left {
|
&-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -155,6 +161,7 @@ setPageTitle(t('breadcrumbs.wishlist'));
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 20px 2px rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: $default_border_radius;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
import { provideApolloClient } from '@vue/apollo-composable'
|
import { useAppConfig } from '~/composables/config';
|
||||||
import type { ApolloClient } from '@apollo/client/core'
|
|
||||||
|
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
const apollo = useApollo()
|
const { COOKIES_LOCALE_KEY } = useAppConfig();
|
||||||
const defaultClient = apollo.clients!.default as ApolloClient<unknown>
|
const localeCookie = useCookie(COOKIES_LOCALE_KEY);
|
||||||
|
const originalFetch = globalThis.fetch;
|
||||||
|
|
||||||
provideApolloClient(defaultClient)
|
globalThis.fetch = (input, init = {}) => {
|
||||||
})
|
const lang = localeCookie.value || 'en-gb';
|
||||||
|
const headers = new Headers(init.headers as any);
|
||||||
|
headers.set('Accept-Language', lang);
|
||||||
|
|
||||||
|
return originalFetch(input, {
|
||||||
|
...init,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
13
storefront/stores/promocodes.ts
Normal file
13
storefront/stores/promocodes.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import type {IPromocode} from "~/types";
|
||||||
|
|
||||||
|
export const usePromocodeStore = defineStore('promocode', () => {
|
||||||
|
const promocodes = ref<{ node: IPromocode }[] | null>(null);
|
||||||
|
const setPromocodes = (promo: { node: IPromocode }[]) => {
|
||||||
|
promocodes.value = promo
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
promocodes,
|
||||||
|
setPromocodes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -12,7 +12,11 @@ export const useUserStore = defineStore('user', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = ref<IUser | null>(null);
|
const user = ref<IUser | null>(null);
|
||||||
|
|
||||||
const isAuthenticated = computed(() => Boolean(cookieAccess.value && user.value));
|
const isAuthenticated = computed(() => Boolean(cookieAccess.value && user.value));
|
||||||
|
const finishedOrdersQuantity = computed(() => {
|
||||||
|
return user.value?.orders.filter((order) => order.status === 'FINISHED').length || 0;
|
||||||
|
});
|
||||||
|
|
||||||
const setUser = (data: IUser | null) => {
|
const setUser = (data: IUser | null) => {
|
||||||
user.value = data;
|
user.value = data;
|
||||||
|
|
@ -21,6 +25,7 @@ export const useUserStore = defineStore('user', () => {
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
setUser,
|
setUser,
|
||||||
isAuthenticated
|
isAuthenticated,
|
||||||
|
finishedOrdersQuantity
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
9
storefront/types/api/promocodes.ts
Normal file
9
storefront/types/api/promocodes.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import type {IPromocode} from "~/types";
|
||||||
|
|
||||||
|
export interface IPromocodesResponse {
|
||||||
|
promocodes: {
|
||||||
|
edges: {
|
||||||
|
node: IPromocode
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,4 +10,16 @@ export interface IUserActivationResponse {
|
||||||
activateUser: {
|
activateUser: {
|
||||||
success: boolean
|
success: boolean
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserUpdatingResponse {
|
||||||
|
updateUser: {
|
||||||
|
user: IUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAvatarUploadResponse {
|
||||||
|
uploadAvatar: {
|
||||||
|
user: IUser
|
||||||
|
}
|
||||||
}
|
}
|
||||||
10
storefront/types/app/promocodes.ts
Normal file
10
storefront/types/app/promocodes.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
export interface IPromocode {
|
||||||
|
code: string,
|
||||||
|
discount: string,
|
||||||
|
discountType: string,
|
||||||
|
endTime: string,
|
||||||
|
id: string,
|
||||||
|
startTime: string,
|
||||||
|
usedOn: string,
|
||||||
|
uuid: string
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export interface IUser {
|
export interface IUser {
|
||||||
avatar: string,
|
avatar: string,
|
||||||
uuid: string,
|
uuid: string,
|
||||||
attributes: string | null
|
attributes: string | null,
|
||||||
language: string,
|
language: string,
|
||||||
email: string,
|
email: string,
|
||||||
firstName: string,
|
firstName: string,
|
||||||
|
|
@ -10,5 +10,10 @@ export interface IUser {
|
||||||
dateJoined: string,
|
dateJoined: string,
|
||||||
balance: {
|
balance: {
|
||||||
amount: number,
|
amount: number,
|
||||||
}
|
},
|
||||||
|
orders: {
|
||||||
|
uuid: string,
|
||||||
|
humanReadableId: string,
|
||||||
|
status: string
|
||||||
|
}[]
|
||||||
}
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ export * from './app/wishlist'
|
||||||
export * from './app/orders'
|
export * from './app/orders'
|
||||||
export * from './app/category'
|
export * from './app/category'
|
||||||
export * from './app/store'
|
export * from './app/store'
|
||||||
|
export * from './app/promocodes'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,4 +27,5 @@ export * from './api/user'
|
||||||
export * from './api/categories'
|
export * from './api/categories'
|
||||||
export * from './api/brands'
|
export * from './api/brands'
|
||||||
export * from './api/contact'
|
export * from './api/contact'
|
||||||
export * from './api/store'
|
export * from './api/store'
|
||||||
|
export * from './api/promocodes'
|
||||||
Loading…
Reference in a new issue