Features: 1) Implement composables for posts, products, categories, languages, and user deposits with lazy loading and GraphQL integration; 2) Add standalone pages for blog, product, store, and profile with scoped SCSS styling; 3) Add reusable UI components including header, footer, input, button, and textarea; 4) Introduce forms for contact and deposit functionality with validation and localization support; 5) Create GraphQL fragments for users, products, categories, company, orders, languages, and wishlist for efficient data fetching;
Fixes: 1) Correct missing semicolons in Pinia store definitions for cart, company, wishlist, and auth stores; 2) Refactor GraphQL queries to include fragments for improved modularity and readability; 3) Correct error handling in composables like `usePosts` and `useLanguages`; Extra: Enhanced App.vue to include dynamic company info and language fetching on mount; Added scoped styles for new components and pages.
This commit is contained in:
parent
f9cc97fb36
commit
9e837ba568
83 changed files with 2048 additions and 683 deletions
|
|
@ -1,12 +1,20 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { RouterView } from 'vue-router'
|
import { RouterView } from 'vue-router'
|
||||||
import {useRefresh} from "@/composables/auth/useRefresh.js";
|
|
||||||
import {onMounted} from "vue";
|
import {onMounted} from "vue";
|
||||||
|
import {useRefresh} from "@/composables/auth";
|
||||||
|
import {useCompanyInfo} from "@/composables/company";
|
||||||
|
import {useLanguages} from "@/composables/languages/index.js";
|
||||||
|
import BaseHeader from "@/components/base/base-header.vue";
|
||||||
|
import BaseFooter from "@/components/base/base-footer.vue";
|
||||||
|
|
||||||
const { refresh } = useRefresh();
|
const { refresh } = useRefresh();
|
||||||
|
const { getCompanyInfo } = useCompanyInfo();
|
||||||
|
const { getLanguages } = useLanguages();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await refresh()
|
await refresh()
|
||||||
|
await getCompanyInfo()
|
||||||
|
await getLanguages()
|
||||||
|
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
await refresh()
|
await refresh()
|
||||||
|
|
@ -16,11 +24,13 @@ onMounted(async () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="main" id="top">
|
<main class="main" id="top">
|
||||||
|
<base-header />
|
||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }">
|
||||||
<Transition name="opacity" mode="out-in">
|
<Transition name="opacity" mode="out-in">
|
||||||
<component :is="Component" />
|
<component :is="Component" />
|
||||||
</Transition>
|
</Transition>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
|
<base-footer />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
19
storefront/src/components/base/base-footer.vue
Normal file
19
storefront/src/components/base/base-footer.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<div class="footer_wrapper">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.footer {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
26
storefront/src/components/base/base-header.vue
Normal file
26
storefront/src/components/base/base-header.vue
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<template>
|
||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header__wrapper">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import {useCategories} from "@/composables/categories";
|
||||||
|
|
||||||
|
const { categories, loading, getCategories } = useCategories();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getCategories()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.header {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
88
storefront/src/components/forms/contact-form.vue
Normal file
88
storefront/src/components/forms/contact-form.vue
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="handleContactUs()" class="form">
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="t('fields.name')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="name"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'email'"
|
||||||
|
:placeholder="t('fields.email')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="email"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="t('fields.phoneNumber')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="phoneNumber"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="t('fields.subject')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="subject"
|
||||||
|
/>
|
||||||
|
<ui-textarea
|
||||||
|
:placeholder="t('fields.message')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="message"
|
||||||
|
/>
|
||||||
|
<ui-button
|
||||||
|
class="form__button"
|
||||||
|
:isDisabled="!isFormValid"
|
||||||
|
:isLoading="loading"
|
||||||
|
>
|
||||||
|
{{ t('buttons.send') }}
|
||||||
|
</ui-button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {required} from "@/core/rules/textFieldRules.js";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import UiInput from "@/components/ui/ui-input.vue";
|
||||||
|
import UiButton from "@/components/ui/ui-button.vue";
|
||||||
|
import UiTextarea from "@/components/ui/ui-textarea.vue";
|
||||||
|
import {useContactUs} from "@/composables/contact/index.js";
|
||||||
|
|
||||||
|
const {t} = useI18n()
|
||||||
|
|
||||||
|
const name = ref('')
|
||||||
|
const email = ref('')
|
||||||
|
const phoneNumber = ref('')
|
||||||
|
const subject = ref('')
|
||||||
|
const message = ref('')
|
||||||
|
|
||||||
|
const isFormValid = computed(() => {
|
||||||
|
return (
|
||||||
|
required(name.value) === true &&
|
||||||
|
required(email.value) === true &&
|
||||||
|
required(phoneNumber.value) === true &&
|
||||||
|
required(subject.value) === true &&
|
||||||
|
required(message.value) === true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const { contactUs, loading } = useContactUs();
|
||||||
|
|
||||||
|
async function handleContactUs() {
|
||||||
|
await contactUs(
|
||||||
|
name.value,
|
||||||
|
email.value,
|
||||||
|
phoneNumber.value,
|
||||||
|
subject.value,
|
||||||
|
message.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
65
storefront/src/components/forms/deposit-form.vue
Normal file
65
storefront/src/components/forms/deposit-form.vue
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="handleDeposit()" class="form">
|
||||||
|
<div class="form__box">
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="''"
|
||||||
|
v-model="amount"
|
||||||
|
:numberOnly="true"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="''"
|
||||||
|
v-model="amount"
|
||||||
|
:numberOnly="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ui-button
|
||||||
|
class="form__button"
|
||||||
|
:isDisabled="!isFormValid"
|
||||||
|
:isLoading="loading"
|
||||||
|
>
|
||||||
|
{{ t('buttons.topUp') }}
|
||||||
|
</ui-button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import UiInput from "@/components/ui/ui-input.vue";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import UiButton from "@/components/ui/ui-button.vue";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {useDeposit} from "@/composables/user/useDeposit.js";
|
||||||
|
|
||||||
|
const {t} = useI18n()
|
||||||
|
|
||||||
|
const amount = ref('')
|
||||||
|
|
||||||
|
const isFormValid = computed(() => {
|
||||||
|
return (
|
||||||
|
amount.value >= 5 && amount.value <= 500
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const onlyNumbersKeypress = (event) => {
|
||||||
|
if (!/\d/.test(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { deposit, loading } = useDeposit();
|
||||||
|
|
||||||
|
async function handleDeposit() {
|
||||||
|
await deposit(amount.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form {
|
||||||
|
&__box {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="form">
|
<form @submit.prevent="handleLogin()" class="form">
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'email'"
|
:type="'email'"
|
||||||
:placeholder="t('fields.email')"
|
:placeholder="t('fields.email')"
|
||||||
|
|
@ -12,15 +12,19 @@
|
||||||
:rules="[required]"
|
:rules="[required]"
|
||||||
v-model="password"
|
v-model="password"
|
||||||
/>
|
/>
|
||||||
|
<ui-checkbox
|
||||||
|
v-model="isStayLogin"
|
||||||
|
>
|
||||||
|
{{ t('checkboxes.remember') }}
|
||||||
|
</ui-checkbox>
|
||||||
<ui-button
|
<ui-button
|
||||||
class="form__button"
|
class="form__button"
|
||||||
:isDisabled="!isFormValid"
|
:isDisabled="!isFormValid"
|
||||||
:isLoading="loading"
|
:isLoading="loading"
|
||||||
@click="handleLogin()"
|
|
||||||
>
|
>
|
||||||
{{ t('buttons.signIn') }}
|
{{ t('buttons.signIn') }}
|
||||||
</ui-button>
|
</ui-button>
|
||||||
</div>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
@ -28,13 +32,15 @@ import {useI18n} from "vue-i18n";
|
||||||
import {isEmail, required} from "@/core/rules/textFieldRules.js";
|
import {isEmail, required} from "@/core/rules/textFieldRules.js";
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import UiInput from "@/components/ui/ui-input.vue";
|
import UiInput from "@/components/ui/ui-input.vue";
|
||||||
import {useLogin} from "@/composables/auth/useLogin.js";
|
|
||||||
import UiButton from "@/components/ui/ui-button.vue";
|
import UiButton from "@/components/ui/ui-button.vue";
|
||||||
|
import UiCheckbox from "@/components/ui/ui-checkbox.vue";
|
||||||
|
import {useLogin} from "@/composables/auth";
|
||||||
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
|
|
||||||
const email = ref('')
|
const email = ref('')
|
||||||
const password = ref('')
|
const password = ref('')
|
||||||
|
const isStayLogin = ref(false)
|
||||||
|
|
||||||
const isFormValid = computed(() => {
|
const isFormValid = computed(() => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -46,7 +52,7 @@ const isFormValid = computed(() => {
|
||||||
const { login, loading } = useLogin();
|
const { login, loading } = useLogin();
|
||||||
|
|
||||||
async function handleLogin() {
|
async function handleLogin() {
|
||||||
await login(email.value, password.value);
|
await login(email.value, password.value, isStayLogin.value);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
66
storefront/src/components/forms/new-password-form.vue
Normal file
66
storefront/src/components/forms/new-password-form.vue
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="handleReset()" class="form">
|
||||||
|
<ui-input
|
||||||
|
:type="'password'"
|
||||||
|
:placeholder="t('fields.newPassword')"
|
||||||
|
:rules="[isPasswordValid]"
|
||||||
|
v-model="password"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'password'"
|
||||||
|
:placeholder="t('fields.confirmNewPassword')"
|
||||||
|
:rules="[compareStrings]"
|
||||||
|
v-model="confirmPassword"
|
||||||
|
/>
|
||||||
|
<ui-button
|
||||||
|
class="form__button"
|
||||||
|
:isDisabled="!isFormValid"
|
||||||
|
:isLoading="loading"
|
||||||
|
>
|
||||||
|
{{ t('buttons.save') }}
|
||||||
|
</ui-button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {isPasswordValid,} from "@/core/rules/textFieldRules.js";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import UiInput from "@/components/ui/ui-input.vue";
|
||||||
|
import UiButton from "@/components/ui/ui-button.vue";
|
||||||
|
import {useNewPassword} from "@/composables/auth";
|
||||||
|
|
||||||
|
const {t} = useI18n()
|
||||||
|
|
||||||
|
const password = ref('')
|
||||||
|
const confirmPassword = ref('')
|
||||||
|
|
||||||
|
const compareStrings = (v) => {
|
||||||
|
if (v === password.value) return true
|
||||||
|
return t('errors.compare')
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFormValid = computed(() => {
|
||||||
|
return (
|
||||||
|
isPasswordValid(password.value) === true &&
|
||||||
|
compareStrings(confirmPassword.value) === true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const { newPassword, loading } = useNewPassword();
|
||||||
|
|
||||||
|
async function handleReset() {
|
||||||
|
await newPassword(
|
||||||
|
password.value,
|
||||||
|
confirmPassword.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="form">
|
<form @submit.prevent="handleRegister()" class="form">
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
:placeholder="t('fields.firstName')"
|
:placeholder="t('fields.firstName')"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
/>
|
/>
|
||||||
<ui-input
|
<ui-input
|
||||||
:type="'text'"
|
:type="'text'"
|
||||||
:placeholder="t('fields.phone')"
|
:placeholder="t('fields.phoneNumber')"
|
||||||
:rules="[required]"
|
:rules="[required]"
|
||||||
v-model="phoneNumber"
|
v-model="phoneNumber"
|
||||||
/>
|
/>
|
||||||
|
|
@ -40,11 +40,10 @@
|
||||||
class="form__button"
|
class="form__button"
|
||||||
:isDisabled="!isFormValid"
|
:isDisabled="!isFormValid"
|
||||||
:isLoading="loading"
|
:isLoading="loading"
|
||||||
@click="handleRegister()"
|
|
||||||
>
|
>
|
||||||
{{ t('buttons.signUp') }}
|
{{ t('buttons.signUp') }}
|
||||||
</ui-button>
|
</ui-button>
|
||||||
</div>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|
@ -53,7 +52,7 @@ import {isEmail, isPasswordValid, required} from "@/core/rules/textFieldRules.js
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import UiInput from "@/components/ui/ui-input.vue";
|
import UiInput from "@/components/ui/ui-input.vue";
|
||||||
import UiButton from "@/components/ui/ui-button.vue";
|
import UiButton from "@/components/ui/ui-button.vue";
|
||||||
import {useRegister} from "@/composables/auth/useRegister.js";
|
import {useRegister} from "@/composables/auth/index.js";
|
||||||
|
|
||||||
const {t} = useI18n()
|
const {t} = useI18n()
|
||||||
|
|
||||||
|
|
|
||||||
50
storefront/src/components/forms/reset-password-form.vue
Normal file
50
storefront/src/components/forms/reset-password-form.vue
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="handleReset()" class="form">
|
||||||
|
<ui-input
|
||||||
|
:type="'email'"
|
||||||
|
:placeholder="t('fields.email')"
|
||||||
|
:rules="[isEmail]"
|
||||||
|
v-model="email"
|
||||||
|
/>
|
||||||
|
<ui-button
|
||||||
|
class="form__button"
|
||||||
|
:isDisabled="!isFormValid"
|
||||||
|
:isLoading="loading"
|
||||||
|
>
|
||||||
|
{{ t('buttons.sendLink') }}
|
||||||
|
</ui-button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {isEmail} from "@/core/rules/textFieldRules.js";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import UiInput from "@/components/ui/ui-input.vue";
|
||||||
|
import UiButton from "@/components/ui/ui-button.vue";
|
||||||
|
import {usePasswordReset} from "@/composables/auth";
|
||||||
|
|
||||||
|
const {t} = useI18n()
|
||||||
|
|
||||||
|
const email = ref('')
|
||||||
|
|
||||||
|
const isFormValid = computed(() => {
|
||||||
|
return (
|
||||||
|
isEmail(email.value) === true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const { resetPassword, loading } = usePasswordReset();
|
||||||
|
|
||||||
|
async function handleReset() {
|
||||||
|
await resetPassword(email.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
104
storefront/src/components/forms/update-form.vue
Normal file
104
storefront/src/components/forms/update-form.vue
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
<template>
|
||||||
|
<form class="form" @submit.prevent="handleUpdate()">
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="t('fields.firstName')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="firstName"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="t('fields.lastName')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="lastName"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'email'"
|
||||||
|
:placeholder="t('fields.email')"
|
||||||
|
:rules="[isEmail]"
|
||||||
|
v-model="email"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'text'"
|
||||||
|
:placeholder="t('fields.phoneNumber')"
|
||||||
|
:rules="[required]"
|
||||||
|
v-model="phoneNumber"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'password'"
|
||||||
|
:placeholder="t('fields.newPassword')"
|
||||||
|
:rules="[isPasswordValid]"
|
||||||
|
v-model="password"
|
||||||
|
/>
|
||||||
|
<ui-input
|
||||||
|
:type="'password'"
|
||||||
|
:placeholder="t('fields.confirmNewPassword')"
|
||||||
|
:rules="[compareStrings]"
|
||||||
|
v-model="confirmPassword"
|
||||||
|
/>
|
||||||
|
<ui-button
|
||||||
|
class="form__button"
|
||||||
|
:isLoading="loading"
|
||||||
|
>
|
||||||
|
{{ t('buttons.save') }}
|
||||||
|
</ui-button>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {isEmail, isPasswordValid, required} from "@/core/rules/textFieldRules.js";
|
||||||
|
import {computed, ref, watchEffect} from "vue";
|
||||||
|
import UiInput from "@/components/ui/ui-input.vue";
|
||||||
|
import UiButton from "@/components/ui/ui-button.vue";
|
||||||
|
import {useAuthStore} from "@/stores/auth.js";
|
||||||
|
import {useUserUpdating} from "@/composables/user";
|
||||||
|
|
||||||
|
const {t} = useI18n()
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
const userFirstName = computed(() => authStore.user?.firstName)
|
||||||
|
const userLastName = computed(() => authStore.user?.lastName)
|
||||||
|
const userEmail = computed(() => authStore.user?.email)
|
||||||
|
const userPhoneNumber = computed(() => authStore.user?.phoneNumber)
|
||||||
|
|
||||||
|
const firstName = ref('')
|
||||||
|
const lastName = ref('')
|
||||||
|
const email = ref('')
|
||||||
|
const phoneNumber = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
const confirmPassword = ref('')
|
||||||
|
|
||||||
|
const compareStrings = (v) => {
|
||||||
|
if (v === password.value) return true
|
||||||
|
return t('errors.compare')
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<button class="button" :disabled="isDisabled" :class="[{active: isLoading}]">
|
<button
|
||||||
|
class="button"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
:class="[{active: isLoading}]"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
<ui-loader class="button__loader" v-if="isLoading" />
|
<ui-loader class="button__loader" v-if="isLoading" />
|
||||||
<slot v-else />
|
<slot v-else />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -24,6 +29,8 @@ const props = defineProps({
|
||||||
border: 1px solid $black;
|
border: 1px solid $black;
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
padding-block: 5px;
|
padding-block: 5px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
color: $black;
|
color: $black;
|
||||||
|
|
@ -33,15 +40,18 @@ const props = defineProps({
|
||||||
|
|
||||||
&:hover, &.active {
|
&:hover, &.active {
|
||||||
background-color: $black;
|
background-color: $black;
|
||||||
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
background-color: rgba($black, 0.5);
|
background-color: rgba($black, 0.5);
|
||||||
|
color: $black;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled:hover, &.active {
|
&:disabled:hover, &.active {
|
||||||
background-color: rgba($black, 0.5);
|
background-color: rgba($black, 0.5);
|
||||||
|
color: $black;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__loader {
|
&__loader {
|
||||||
|
|
|
||||||
67
storefront/src/components/ui/ui-checkbox.vue
Normal file
67
storefront/src/components/ui/ui-checkbox.vue
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
:id="'checkbox' + id"
|
||||||
|
class="checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
:value="modelValue"
|
||||||
|
@input="onInput"
|
||||||
|
:checked="modelValue"
|
||||||
|
>
|
||||||
|
<label :for="'checkbox' + id" class="checkbox__label">
|
||||||
|
<slot />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const $emit = defineEmits()
|
||||||
|
const props = defineProps({
|
||||||
|
id: [Number, String],
|
||||||
|
modelValue: Boolean
|
||||||
|
})
|
||||||
|
|
||||||
|
const onInput = (event) => {
|
||||||
|
$emit('update:modelValue', event.target.checked);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.checkbox {
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
color: #2B2B2B;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px;
|
||||||
|
letter-spacing: 0.12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox + .checkbox__label::before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 17px;
|
||||||
|
height: 17px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
border: 1px solid $black;
|
||||||
|
margin-right: 10px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
background-size: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox:checked + .checkbox__label::before {
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2f6b4f' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e");
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox + .checkbox__label {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
:type="isPasswordVisible"
|
:type="isPasswordVisible"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
|
@keydown="numberOnly ? onlyNumbersKeydown($event) : null"
|
||||||
class="block__input"
|
class="block__input"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
|
@ -31,7 +32,8 @@ const props = defineProps({
|
||||||
isError: Boolean,
|
isError: Boolean,
|
||||||
error: String,
|
error: String,
|
||||||
modelValue: [String, Number],
|
modelValue: [String, Number],
|
||||||
rules: Array
|
rules: Array,
|
||||||
|
numberOnly: Boolean
|
||||||
})
|
})
|
||||||
|
|
||||||
const isPasswordVisible = ref(props.type)
|
const isPasswordVisible = ref(props.type)
|
||||||
|
|
@ -43,9 +45,26 @@ const setPasswordVisible = () => {
|
||||||
isPasswordVisible.value = 'password'
|
isPasswordVisible.value = 'password'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onlyNumbersKeydown = (event) => {
|
||||||
|
if (!/^\d$/.test(event.key) &&
|
||||||
|
!['ArrowLeft', 'ArrowRight', 'Backspace', 'Delete', 'Tab', 'Home', 'End'].includes(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const validate = ref(true)
|
const validate = ref(true)
|
||||||
const errorMessage = ref('')
|
const errorMessage = ref('')
|
||||||
const onInput = (e) => {
|
const onInput = (e) => {
|
||||||
|
let value = e.target.value;
|
||||||
|
|
||||||
|
if (props.numberOnly) {
|
||||||
|
const newValue = value.replace(/\D/g, '');
|
||||||
|
if (newValue !== value) {
|
||||||
|
e.target.value = newValue;
|
||||||
|
value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let result = true
|
let result = true
|
||||||
|
|
||||||
props.rules?.forEach((rule) => {
|
props.rules?.forEach((rule) => {
|
||||||
|
|
@ -87,7 +106,6 @@ const onInput = (e) => {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
letter-spacing: 0.14px;
|
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: #2B2B2B;
|
color: #2B2B2B;
|
||||||
|
|
|
||||||
90
storefront/src/components/ui/ui-textarea.vue
Normal file
90
storefront/src/components/ui/ui-textarea.vue
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<textarea
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="modelValue"
|
||||||
|
@input="onInput"
|
||||||
|
class="block__textarea"
|
||||||
|
/>
|
||||||
|
<p v-if="!validate" class="block__error">{{ errorMessage }}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {ref} from "vue";
|
||||||
|
|
||||||
|
const $emit = defineEmits()
|
||||||
|
const props = defineProps({
|
||||||
|
placeholder: String,
|
||||||
|
isError: Boolean,
|
||||||
|
error: String,
|
||||||
|
modelValue: [String, Number],
|
||||||
|
rules: Array
|
||||||
|
})
|
||||||
|
|
||||||
|
const validate = ref(true)
|
||||||
|
const errorMessage = ref('')
|
||||||
|
const onInput = (e) => {
|
||||||
|
let result = true
|
||||||
|
|
||||||
|
props.rules?.forEach((rule) => {
|
||||||
|
result = rule((e.target).value)
|
||||||
|
|
||||||
|
if (result !== true) {
|
||||||
|
errorMessage.value = String(result)
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
validate.value = result
|
||||||
|
|
||||||
|
return $emit('update:modelValue', (e.target).value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.block {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&__textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 150px;
|
||||||
|
resize: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid $black;
|
||||||
|
background-color: $white;
|
||||||
|
|
||||||
|
color: #1f1f1f;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #2B2B2B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__error {
|
||||||
|
color: $error;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
animation: fadeInUp 0.3s ease;
|
||||||
|
|
||||||
|
@keyframes fadeInUp {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
6
storefront/src/composables/auth/index.js
Normal file
6
storefront/src/composables/auth/index.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export * from './useLogin';
|
||||||
|
export * from './useLogout';
|
||||||
|
export * from './useNewPassword';
|
||||||
|
export * from './usePasswordReset';
|
||||||
|
export * from './useRefresh';
|
||||||
|
export * from './useRegister';
|
||||||
|
|
@ -5,25 +5,33 @@ import {ElNotification} from "element-plus";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
import {useAuthStore} from "@/stores/auth.js";
|
import {useAuthStore} from "@/stores/auth.js";
|
||||||
import translations from "@/core/helpers/translations.js";
|
import translations from "@/core/helpers/translations.js";
|
||||||
import {LOCALE_STORAGE_REFRESH_KEY} from "@/config/index.js";
|
import {
|
||||||
import { useAuthOrder } from './useAuthOrder';
|
DEFAULT_LOCALE,
|
||||||
import { useAuthWishlist } from './useAuthWishlist';
|
LOCALE_STORAGE_LOCALE_KEY,
|
||||||
|
LOCALE_STORAGE_REFRESH_KEY,
|
||||||
|
LOCALE_STORAGE_STAY_LOGIN_KEY
|
||||||
|
} from "@/config/index.js";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
import {usePendingOrder} from "@/composables/orders";
|
||||||
|
import {useWishlist} from "@/composables/wishlist";
|
||||||
|
|
||||||
export function useLogin() {
|
export function useLogin() {
|
||||||
const loading = ref(false);
|
const router = useRouter();
|
||||||
const userData = ref(null);
|
const route = useRoute();
|
||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
|
|
||||||
const { mutate: loginMutation } = useMutation(LOGIN);
|
const { mutate: loginMutation } = useMutation(LOGIN);
|
||||||
|
|
||||||
const { getPendingOrder } = useAuthOrder();
|
const { getPendingOrder } = usePendingOrder();
|
||||||
const { getWishlist } = useAuthWishlist();
|
const { getWishlist } = useWishlist();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
async function login(
|
async function login(
|
||||||
email,
|
email,
|
||||||
password
|
password,
|
||||||
|
isStayLogin
|
||||||
) {
|
) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
|
|
@ -33,6 +41,10 @@ export function useLogin() {
|
||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isStayLogin) {
|
||||||
|
localStorage.setItem(LOCALE_STORAGE_STAY_LOGIN_KEY, 'remember')
|
||||||
|
}
|
||||||
|
|
||||||
if (response.data?.obtainJwtToken) {
|
if (response.data?.obtainJwtToken) {
|
||||||
authStore.setUser({
|
authStore.setUser({
|
||||||
user: response.data.obtainJwtToken.user,
|
user: response.data.obtainJwtToken.user,
|
||||||
|
|
@ -42,26 +54,33 @@ export function useLogin() {
|
||||||
localStorage.setItem(LOCALE_STORAGE_REFRESH_KEY, response.data.obtainJwtToken.refreshToken)
|
localStorage.setItem(LOCALE_STORAGE_REFRESH_KEY, response.data.obtainJwtToken.refreshToken)
|
||||||
|
|
||||||
ElNotification({
|
ElNotification({
|
||||||
message: t('popup.login.text'),
|
message: t('popup.success.login'),
|
||||||
type: 'success'
|
type: 'success'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await router.push({
|
||||||
|
name: 'home',
|
||||||
|
params: {
|
||||||
|
locale: localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY) || DEFAULT_LOCALE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (response.data.obtainJwtToken.user.language !== translations.currentLocale) {
|
if (response.data.obtainJwtToken.user.language !== translations.currentLocale) {
|
||||||
translations.switchLanguage(response.data.obtainJwtToken.user.language)
|
translations.switchLanguage(response.data.obtainJwtToken.user.language, router, route)
|
||||||
}
|
}
|
||||||
|
|
||||||
await getPendingOrder(response.data.obtainJwtToken.user.email);
|
await getPendingOrder(response.data.obtainJwtToken.user.email);
|
||||||
await getWishlist();
|
await getWishlist();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Login error:", error);
|
console.error("useLogin error:", error);
|
||||||
|
|
||||||
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
error.message ||
|
error.message ||
|
||||||
t('popup.genericError');
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: t('popup.error'),
|
title: t('popup.errors.main'),
|
||||||
message: errorMessage,
|
message: errorMessage,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
|
|
@ -72,7 +91,6 @@ export function useLogin() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
login,
|
login,
|
||||||
loading,
|
loading
|
||||||
userData
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
34
storefront/src/composables/auth/useLogout.js
Normal file
34
storefront/src/composables/auth/useLogout.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import {useAuthStore} from "@/stores/auth.js";
|
||||||
|
import {
|
||||||
|
DEFAULT_LOCALE,
|
||||||
|
LOCALE_STORAGE_LOCALE_KEY,
|
||||||
|
LOCALE_STORAGE_REFRESH_KEY,
|
||||||
|
LOCALE_STORAGE_STAY_LOGIN_KEY
|
||||||
|
} from "@/config/index.js";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
|
||||||
|
export function useLogout() {
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
authStore.setUser({
|
||||||
|
user: null,
|
||||||
|
accessToken: null
|
||||||
|
})
|
||||||
|
|
||||||
|
localStorage.removeItem(LOCALE_STORAGE_REFRESH_KEY)
|
||||||
|
localStorage.removeItem(LOCALE_STORAGE_STAY_LOGIN_KEY)
|
||||||
|
|
||||||
|
await router.push({
|
||||||
|
name: 'home',
|
||||||
|
params: {
|
||||||
|
locale: localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY) || DEFAULT_LOCALE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
logout
|
||||||
|
};
|
||||||
|
}
|
||||||
73
storefront/src/composables/auth/useNewPassword.js
Normal file
73
storefront/src/composables/auth/useNewPassword.js
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import {useMutation} from "@vue/apollo-composable";
|
||||||
|
import {NEW_PASSWORD} from "@/graphql/mutations/auth.js";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
import {DEFAULT_LOCALE, LOCALE_STORAGE_LOCALE_KEY} from "@/config/index.js";
|
||||||
|
|
||||||
|
export function useNewPassword() {
|
||||||
|
const {t} = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const { mutate: newPasswordMutation } = useMutation(NEW_PASSWORD);
|
||||||
|
|
||||||
|
const token = computed(() =>
|
||||||
|
route.query.token ? (route.query.token) : undefined,
|
||||||
|
);
|
||||||
|
const uid = computed(() =>
|
||||||
|
route.query.uid ? (route.query.uid) : undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function newPassword(
|
||||||
|
password,
|
||||||
|
confirmPassword
|
||||||
|
) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await newPasswordMutation({
|
||||||
|
password,
|
||||||
|
confirmPassword,
|
||||||
|
token: token.value,
|
||||||
|
uid: uid.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data?.confirmResetPassword.success) {
|
||||||
|
ElNotification({
|
||||||
|
message: t('popup.success.newPassword'),
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
await router.push({
|
||||||
|
name: 'home',
|
||||||
|
params: {
|
||||||
|
locale: localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY) || DEFAULT_LOCALE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("useNewPassword error:", error);
|
||||||
|
|
||||||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
|
error.message ||
|
||||||
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
newPassword,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
51
storefront/src/composables/auth/usePasswordReset.js
Normal file
51
storefront/src/composables/auth/usePasswordReset.js
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {useMutation} from "@vue/apollo-composable";
|
||||||
|
import {RESET_PASSWORD} from "@/graphql/mutations/auth.js";
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
|
||||||
|
export function usePasswordReset() {
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
const { mutate: resetPasswordMutation } = useMutation(RESET_PASSWORD);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function resetPassword(
|
||||||
|
email
|
||||||
|
) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await resetPasswordMutation({
|
||||||
|
email
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data?.resetPassword.success) {
|
||||||
|
ElNotification({
|
||||||
|
message: t('popup.success.reset'),
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("usePasswordReset error:", error);
|
||||||
|
|
||||||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
|
error.message ||
|
||||||
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
resetPassword,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -4,16 +4,13 @@ import {computed, ref} from "vue";
|
||||||
import {ElNotification} from "element-plus";
|
import {ElNotification} from "element-plus";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
import {useAuthStore} from "@/stores/auth.js";
|
import {useAuthStore} from "@/stores/auth.js";
|
||||||
import { useAuthOrder } from './useAuthOrder';
|
import {LOCALE_STORAGE_REFRESH_KEY} from "@/config/index.js";
|
||||||
import { useAuthWishlist } from './useAuthWishlist';
|
|
||||||
import {DEFAULT_LOCALE, LOCALE_STORAGE_LOCALE_KEY, LOCALE_STORAGE_REFRESH_KEY} from "@/config/index.js";
|
|
||||||
import {useRoute, useRouter} from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import translations from "@/core/helpers/translations.js";
|
import translations from "@/core/helpers/translations.js";
|
||||||
|
import {usePendingOrder} from "@/composables/orders";
|
||||||
|
import {useWishlist} from "@/composables/wishlist";
|
||||||
|
|
||||||
export function useRefresh() {
|
export function useRefresh() {
|
||||||
const loading = ref(false);
|
|
||||||
const userData = ref(null);
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
@ -21,15 +18,15 @@ export function useRefresh() {
|
||||||
|
|
||||||
const { mutate: refreshMutation } = useMutation(REFRESH);
|
const { mutate: refreshMutation } = useMutation(REFRESH);
|
||||||
|
|
||||||
const { getPendingOrder } = useAuthOrder();
|
const { getPendingOrder } = usePendingOrder();
|
||||||
const { getWishlist } = useAuthWishlist();
|
const { getWishlist } = useWishlist();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
async function refresh() {
|
async function refresh() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
const refreshToken = computed(() => {
|
const refreshToken = computed(() => localStorage.getItem(LOCALE_STORAGE_REFRESH_KEY))
|
||||||
return localStorage.getItem(LOCALE_STORAGE_REFRESH_KEY)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!refreshToken.value) return
|
if (!refreshToken.value) return
|
||||||
|
|
||||||
|
|
@ -45,13 +42,7 @@ export function useRefresh() {
|
||||||
})
|
})
|
||||||
|
|
||||||
if (response.data.refreshJwtToken.user.language !== translations.currentLocale) {
|
if (response.data.refreshJwtToken.user.language !== translations.currentLocale) {
|
||||||
translations.switchLanguage(response.data.refreshJwtToken.user.language)
|
translations.switchLanguage(response.data.refreshJwtToken.user.language, router, route)
|
||||||
await router.push({
|
|
||||||
name: route.name,
|
|
||||||
params: {
|
|
||||||
locale: localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY) || DEFAULT_LOCALE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.setItem(LOCALE_STORAGE_REFRESH_KEY, response.data.refreshJwtToken.refreshToken)
|
localStorage.setItem(LOCALE_STORAGE_REFRESH_KEY, response.data.refreshJwtToken.refreshToken)
|
||||||
|
|
@ -60,14 +51,14 @@ export function useRefresh() {
|
||||||
await getWishlist();
|
await getWishlist();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Refresh error:", error);
|
console.error("useRefresh error:", error);
|
||||||
|
|
||||||
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
error.message ||
|
error.message ||
|
||||||
t('popup.genericError');
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: t('popup.error'),
|
title: t('popup.errors.main'),
|
||||||
message: errorMessage,
|
message: errorMessage,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
|
|
@ -78,7 +69,6 @@ export function useRefresh() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
refresh,
|
refresh,
|
||||||
loading,
|
loading
|
||||||
userData
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -3,18 +3,17 @@ import {REGISTER} from "@/graphql/mutations/auth.js";
|
||||||
import {h, ref} from "vue";
|
import {h, ref} from "vue";
|
||||||
import {ElNotification} from "element-plus";
|
import {ElNotification} from "element-plus";
|
||||||
import {useI18n} from "vue-i18n";
|
import {useI18n} from "vue-i18n";
|
||||||
import {useMailClient} from "@/composables/auth/useMainClient.js";
|
import {useMailClient} from "@/composables/utils";
|
||||||
|
|
||||||
export function useRegister() {
|
export function useRegister() {
|
||||||
const loading = ref(false);
|
|
||||||
const mailClient = ref(null)
|
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
|
|
||||||
const { mutate: registerMutation } = useMutation(REGISTER);
|
const { mutate: registerMutation } = useMutation(REGISTER);
|
||||||
|
|
||||||
const { mailClientUrl, detectMailClient, openMailClient } = useMailClient();
|
const { mailClientUrl, detectMailClient, openMailClient } = useMailClient();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
async function register(
|
async function register(
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
|
|
@ -39,9 +38,8 @@ export function useRegister() {
|
||||||
detectMailClient(email);
|
detectMailClient(email);
|
||||||
|
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: t('popup.register.title'),
|
|
||||||
message: h('div', [
|
message: h('div', [
|
||||||
h('p', t('popup.register.text')),
|
h('p', t('popup.success.register')),
|
||||||
mailClientUrl.value ? h(
|
mailClientUrl.value ? h(
|
||||||
'button',
|
'button',
|
||||||
{
|
{
|
||||||
|
|
@ -64,14 +62,14 @@ export function useRegister() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Register error:", error);
|
console.error("useRegister error:", error);
|
||||||
|
|
||||||
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
error.message ||
|
error.message ||
|
||||||
t('popup.genericError');
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: t('popup.error'),
|
title: t('popup.errors.main'),
|
||||||
message: errorMessage,
|
message: errorMessage,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
});
|
});
|
||||||
|
|
|
||||||
1
storefront/src/composables/blog/index.js
Normal file
1
storefront/src/composables/blog/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './usePosts'
|
||||||
24
storefront/src/composables/blog/usePostBySlug.js
Normal file
24
storefront/src/composables/blog/usePostBySlug.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useLazyQuery } from "@vue/apollo-composable";
|
||||||
|
import {GET_POST_BY_SLUG} from "@/graphql/queries/blog.js";
|
||||||
|
import {computed} from "vue";
|
||||||
|
|
||||||
|
export function usePostbySlug() {
|
||||||
|
const { result, loading, error, load } = useLazyQuery(GET_POST_BY_SLUG);
|
||||||
|
|
||||||
|
const post = computed(() => result.value?.posts.edges[0].node ?? []);
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error("usePostbySlug error:", error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPost = (slug) => {
|
||||||
|
return load(null, { slug });
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
post,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getPost
|
||||||
|
};
|
||||||
|
}
|
||||||
20
storefront/src/composables/blog/usePosts.js
Normal file
20
storefront/src/composables/blog/usePosts.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { useLazyQuery } from "@vue/apollo-composable";
|
||||||
|
import { GET_POSTS } from "@/graphql/queries/blog.js";
|
||||||
|
import {computed} from "vue";
|
||||||
|
|
||||||
|
export function usePosts() {
|
||||||
|
const { result, loading, error, load } = useLazyQuery(GET_POSTS);
|
||||||
|
|
||||||
|
const posts = computed(() => result.value?.posts.edges ?? []);
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error("usePosts error:", error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
posts,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getPosts: load
|
||||||
|
};
|
||||||
|
}
|
||||||
2
storefront/src/composables/categories/index.js
Normal file
2
storefront/src/composables/categories/index.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './useCategories'
|
||||||
|
export * from './useCategorybySlug'
|
||||||
20
storefront/src/composables/categories/useCategories.js
Normal file
20
storefront/src/composables/categories/useCategories.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { useLazyQuery } from "@vue/apollo-composable";
|
||||||
|
import {computed} from "vue";
|
||||||
|
import {GET_CATEGORIES} from "@/graphql/queries/categories.js";
|
||||||
|
|
||||||
|
export function useCategories() {
|
||||||
|
const { result, loading, error, load } = useLazyQuery(GET_CATEGORIES);
|
||||||
|
|
||||||
|
const categories = computed(() => result.value?.categories.edges ?? []);
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error("useCategories error:", error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
categories,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getCategories: load
|
||||||
|
};
|
||||||
|
}
|
||||||
24
storefront/src/composables/categories/useCategorybySlug.js
Normal file
24
storefront/src/composables/categories/useCategorybySlug.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useLazyQuery } from "@vue/apollo-composable";
|
||||||
|
import {computed} from "vue";
|
||||||
|
import {GET_CATEGORY_BY_SLUG} from "@/graphql/queries/categories.js";
|
||||||
|
|
||||||
|
export function usePostbySlug() {
|
||||||
|
const { result, loading, error, load } = useLazyQuery(GET_CATEGORY_BY_SLUG);
|
||||||
|
|
||||||
|
const category = computed(() => result.value?.categories.edges[0].node ?? []);
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error("usePostbySlug error:", error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCategory = (slug) => {
|
||||||
|
return load(null, { slug });
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
category,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getCategory
|
||||||
|
};
|
||||||
|
}
|
||||||
1
storefront/src/composables/company/index.js
Normal file
1
storefront/src/composables/company/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useCompanyInfo';
|
||||||
26
storefront/src/composables/company/useCompanyInfo.js
Normal file
26
storefront/src/composables/company/useCompanyInfo.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {useLazyQuery} from "@vue/apollo-composable";
|
||||||
|
import {GET_COMPANY_INFO} from "@/graphql/queries/company.js";
|
||||||
|
import {useCompanyStore} from "@/stores/company.js";
|
||||||
|
import {watchEffect} from "vue";
|
||||||
|
|
||||||
|
export function useCompanyInfo() {
|
||||||
|
const companyStore = useCompanyStore()
|
||||||
|
|
||||||
|
const { result, loading, error, load } = useLazyQuery(GET_COMPANY_INFO);
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error("useCompanyInfo error:", error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (result.value?.parameters) {
|
||||||
|
companyStore.setCompanyInfo(result.value.parameters);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getCompanyInfo: load
|
||||||
|
};
|
||||||
|
}
|
||||||
1
storefront/src/composables/contact/index.js
Normal file
1
storefront/src/composables/contact/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useContactUs'
|
||||||
59
storefront/src/composables/contact/useContactUs.js
Normal file
59
storefront/src/composables/contact/useContactUs.js
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import {useMutation} from "@vue/apollo-composable";
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {CONTACT_US} from "@/graphql/mutations/contact.js";
|
||||||
|
|
||||||
|
export function useContactUs() {
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
const { mutate: contactUsMutation } = useMutation(CONTACT_US);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function contactUs(
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
phoneNumber,
|
||||||
|
subject,
|
||||||
|
message
|
||||||
|
) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await contactUsMutation({
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
phoneNumber,
|
||||||
|
subject,
|
||||||
|
message
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data?.contactUs.received) {
|
||||||
|
ElNotification({
|
||||||
|
message: t('popup.success.contactUs'),
|
||||||
|
type: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("useContactUs error:", error);
|
||||||
|
|
||||||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
|
error.message ||
|
||||||
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
contactUs,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
2
storefront/src/composables/languages/index.js
Normal file
2
storefront/src/composables/languages/index.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './useLanguageSwitch.js'
|
||||||
|
export * from './useLanguages'
|
||||||
61
storefront/src/composables/languages/useLanguageSwitch.js
Normal file
61
storefront/src/composables/languages/useLanguageSwitch.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import {useMutation} from "@vue/apollo-composable";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {useAuthStore} from "@/stores/auth.js";
|
||||||
|
import translations from "@/core/helpers/translations.js";
|
||||||
|
import {SWITCH_LANGUAGE} from "@/graphql/mutations/languages.js";
|
||||||
|
|
||||||
|
export function useLanguageSwitch() {
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
const { mutate: languageSwitchMutation } = useMutation(SWITCH_LANGUAGE);
|
||||||
|
|
||||||
|
const accessToken = computed(() => authStore.accessToken)
|
||||||
|
const userUuid = computed(() => authStore.user?.uuid)
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function switchLanguage(
|
||||||
|
locale
|
||||||
|
) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
translations.switchLanguage(locale)
|
||||||
|
if (accessToken.value) {
|
||||||
|
const response = await languageSwitchMutation(
|
||||||
|
userUuid.value,
|
||||||
|
locale
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data?.updateUser) {
|
||||||
|
authStore.setUser({
|
||||||
|
user: response.data.updateUser.user,
|
||||||
|
accessToken: accessToken.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("useLanguageSet error:", error);
|
||||||
|
|
||||||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
|
error.message ||
|
||||||
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
switchLanguage,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
33
storefront/src/composables/languages/useLanguages.js
Normal file
33
storefront/src/composables/languages/useLanguages.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { useLazyQuery } from "@vue/apollo-composable";
|
||||||
|
import {watchEffect} from "vue";
|
||||||
|
import {GET_LANGUAGES} from "@/graphql/queries/languages.js";
|
||||||
|
import {useLanguageStore} from "@/stores/languages.js";
|
||||||
|
import {SUPPORTED_LOCALES} from "@/config/index.js";
|
||||||
|
|
||||||
|
export function useLanguages() {
|
||||||
|
const languageStore = useLanguageStore()
|
||||||
|
|
||||||
|
const { result, loading, error, load } = useLazyQuery(GET_LANGUAGES);
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error("useLanguages error:", error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (result.value?.languages) {
|
||||||
|
languageStore.setLanguages(
|
||||||
|
result.value.languages.filter((locale) =>
|
||||||
|
SUPPORTED_LOCALES.some(supportedLocale =>
|
||||||
|
supportedLocale.code === locale.code
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getLanguages: load
|
||||||
|
};
|
||||||
|
}
|
||||||
1
storefront/src/composables/orders/index.js
Normal file
1
storefront/src/composables/orders/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './usePendingOrder';
|
||||||
|
|
@ -2,7 +2,7 @@ import {useMutation} from "@vue/apollo-composable";
|
||||||
import {GET_ORDERS} from "@/graphql/queries/orders.js";
|
import {GET_ORDERS} from "@/graphql/queries/orders.js";
|
||||||
import {useCartStore} from "@/stores/cart.js";
|
import {useCartStore} from "@/stores/cart.js";
|
||||||
|
|
||||||
export function useAuthOrder() {
|
export function usePendingOrder() {
|
||||||
const cartStore = useCartStore()
|
const cartStore = useCartStore()
|
||||||
|
|
||||||
const { mutate: pendingOrderMutation } = useMutation(GET_ORDERS);
|
const { mutate: pendingOrderMutation } = useMutation(GET_ORDERS);
|
||||||
1
storefront/src/composables/products/index.js
Normal file
1
storefront/src/composables/products/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useProducts'
|
||||||
24
storefront/src/composables/products/useProductBySlug.js
Normal file
24
storefront/src/composables/products/useProductBySlug.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useLazyQuery } from "@vue/apollo-composable";
|
||||||
|
import {computed} from "vue";
|
||||||
|
import {GET_PRODUCT_BY_SLUG} from "@/graphql/queries/products.js";
|
||||||
|
|
||||||
|
export function useProductbySlug() {
|
||||||
|
const { result, loading, error, load } = useLazyQuery(GET_PRODUCT_BY_SLUG);
|
||||||
|
|
||||||
|
const product = computed(() => result.value?.products.edges[0].node ?? []);
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
console.error("useProductbySlug error:", error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProduct = (slug) => {
|
||||||
|
return load(null, { slug });
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
product,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
getProduct
|
||||||
|
};
|
||||||
|
}
|
||||||
53
storefront/src/composables/products/useProducts.js
Normal file
53
storefront/src/composables/products/useProducts.js
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useQuery } from '@vue/apollo-composable';
|
||||||
|
import {GET_PRODUCTS} from "@/graphql/queries/products.js";
|
||||||
|
|
||||||
|
export function useProducts() {
|
||||||
|
const products = ref([]);
|
||||||
|
const pageInfo = ref([]);
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const getProducts = async (params = {}) => {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
const defaults = {
|
||||||
|
first: 12
|
||||||
|
};
|
||||||
|
|
||||||
|
const variables = {};
|
||||||
|
|
||||||
|
Object.entries(params).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
variables[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.entries(defaults).forEach(([key, value]) => {
|
||||||
|
if (!(key in variables)) {
|
||||||
|
variables[key] = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { onResult } = useQuery(GET_PRODUCTS, variables);
|
||||||
|
|
||||||
|
onResult(result => {
|
||||||
|
if (result.data && result.data.products) {
|
||||||
|
products.value = result.data.products.edges;
|
||||||
|
pageInfo.value = result.data.products.pageInfo;
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('useProducts error:', error);
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
products,
|
||||||
|
pageInfo,
|
||||||
|
loading,
|
||||||
|
getProducts
|
||||||
|
};
|
||||||
|
}
|
||||||
3
storefront/src/composables/user/index.js
Normal file
3
storefront/src/composables/user/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './useUserUpdating';
|
||||||
|
export * from './useUserActivation';
|
||||||
|
export * from '../languages/useLanguageSwitch.js';
|
||||||
48
storefront/src/composables/user/useDeposit.js
Normal file
48
storefront/src/composables/user/useDeposit.js
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import {useMutation} from "@vue/apollo-composable";
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {DEPOSIT} from "@/graphql/mutations/deposit.js";
|
||||||
|
|
||||||
|
export function useDeposit() {
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
const { mutate: depositMutation } = useMutation(DEPOSIT);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function deposit(
|
||||||
|
amount
|
||||||
|
) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await depositMutation(
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data?.deposit) {
|
||||||
|
window.open(response.data.deposit.transaction.process.url)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("useDeposit error:", error);
|
||||||
|
|
||||||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
|
error.message ||
|
||||||
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
deposit,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
59
storefront/src/composables/user/useUserActivation.js
Normal file
59
storefront/src/composables/user/useUserActivation.js
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import {useMutation} from "@vue/apollo-composable";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
import {ACTIVATE_USER} from "@/graphql/mutations/user.js";
|
||||||
|
|
||||||
|
export function useUserActivation() {
|
||||||
|
const {t} = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const { mutate: userActivationMutation } = useMutation(ACTIVATE_USER);
|
||||||
|
|
||||||
|
const token = computed(() =>
|
||||||
|
route.query.token ? (route.query.token) : undefined,
|
||||||
|
);
|
||||||
|
const uid = computed(() =>
|
||||||
|
route.query.uid ? (route.query.uid) : undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function activateUser() {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await userActivationMutation({
|
||||||
|
token: token.value,
|
||||||
|
uid: uid.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data?.activateUser) {
|
||||||
|
ElNotification({
|
||||||
|
message: t("popup.activationSuccess"),
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("useUserActivation error:", error);
|
||||||
|
|
||||||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
|
error.message ||
|
||||||
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
activateUser,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
120
storefront/src/composables/user/useUserUpdating.js
Normal file
120
storefront/src/composables/user/useUserUpdating.js
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
import {useMutation} from "@vue/apollo-composable";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {ElNotification} from "element-plus";
|
||||||
|
import {useI18n} from "vue-i18n";
|
||||||
|
import {useAuthStore} from "@/stores/auth.js";
|
||||||
|
import translations from "@/core/helpers/translations.js";
|
||||||
|
import {useRoute, useRouter} from "vue-router";
|
||||||
|
import {useLogout} from "@/composables/auth";
|
||||||
|
import {UPDATE_USER} from "@/graphql/mutations/user.js";
|
||||||
|
|
||||||
|
export function useUserUpdating() {
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
const {t} = useI18n();
|
||||||
|
|
||||||
|
const { mutate: userUpdatingMutation } = useMutation(UPDATE_USER);
|
||||||
|
|
||||||
|
const { logout } = useLogout();
|
||||||
|
|
||||||
|
const accessToken = computed(() => authStore.accessToken)
|
||||||
|
const userUuid = computed(() => authStore.user?.uuid)
|
||||||
|
const userEmail = computed(() => authStore.user?.email)
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
async function updateUser(
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
email,
|
||||||
|
phoneNumber,
|
||||||
|
password,
|
||||||
|
confirmPassword
|
||||||
|
) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
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) {
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: t('popup.errors.noDataToUpdate'),
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await userUpdatingMutation(
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data?.updateUser) {
|
||||||
|
if (userEmail.value !== email) {
|
||||||
|
await logout()
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
message: t("popup.success.confirmEmail"),
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
authStore.setUser({
|
||||||
|
user: response.data.updateUser.user,
|
||||||
|
accessToken: accessToken.value
|
||||||
|
})
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
message: t("popup.successUpdate"),
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.updateUser.user.language !== translations.currentLocale) {
|
||||||
|
translations.switchLanguage(response.data.updateUser.user.language, router, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("useUserUpdating error:", error);
|
||||||
|
|
||||||
|
const errorMessage = error.graphQLErrors?.[0]?.message ||
|
||||||
|
error.message ||
|
||||||
|
t('popup.errors.defaultError');
|
||||||
|
|
||||||
|
ElNotification({
|
||||||
|
title: t('popup.errors.main'),
|
||||||
|
message: errorMessage,
|
||||||
|
type: 'error'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateUser,
|
||||||
|
loading
|
||||||
|
};
|
||||||
|
}
|
||||||
1
storefront/src/composables/utils/index.js
Normal file
1
storefront/src/composables/utils/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useMainClient';
|
||||||
|
|
@ -6,8 +6,12 @@ export function useMailClient() {
|
||||||
const mailClients = {
|
const mailClients = {
|
||||||
'gmail.com': 'https://mail.google.com/',
|
'gmail.com': 'https://mail.google.com/',
|
||||||
'outlook.com': 'https://outlook.live.com/',
|
'outlook.com': 'https://outlook.live.com/',
|
||||||
'icloud.com': 'https://www.icloud.com/',
|
'icloud.com': 'https://www.icloud.com/mail/',
|
||||||
'yahoo.com': 'https://mail.yahoo.com/'
|
'yahoo.com': 'https://mail.yahoo.com/',
|
||||||
|
'mail.ru': 'https://e.mail.ru/inbox/',
|
||||||
|
'yandex.ru': 'https://mail.yandex.ru/',
|
||||||
|
'proton.me': 'https://account.proton.me/mail',
|
||||||
|
'fastmail.com': 'https://fastmail.com/'
|
||||||
};
|
};
|
||||||
|
|
||||||
function detectMailClient(email) {
|
function detectMailClient(email) {
|
||||||
1
storefront/src/composables/wishlist/index.js
Normal file
1
storefront/src/composables/wishlist/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './useWishlist';
|
||||||
|
|
@ -2,7 +2,7 @@ import {useMutation} from "@vue/apollo-composable";
|
||||||
import {GET_WISHLIST} from "@/graphql/queries/wishlist.js";
|
import {GET_WISHLIST} from "@/graphql/queries/wishlist.js";
|
||||||
import {useWishlistStore} from "@/stores/wishlist.js";
|
import {useWishlistStore} from "@/stores/wishlist.js";
|
||||||
|
|
||||||
export function useAuthWishlist() {
|
export function useWishlist() {
|
||||||
const wishlistStore = useWishlistStore()
|
const wishlistStore = useWishlistStore()
|
||||||
|
|
||||||
const { mutate: wishlistMutation } = useMutation(GET_WISHLIST);
|
const { mutate: wishlistMutation } = useMutation(GET_WISHLIST);
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
export const APP_NAME = import.meta.env.EVIBES_PROJECT_NAME
|
export const APP_NAME = import.meta.env.EVIBES_PROJECT_NAME
|
||||||
|
|
||||||
export const APP_NAME_KEY = import.meta.env.EVIBES_PROJECT_NAME.toLowerCase()
|
export const APP_NAME_KEY = APP_NAME.toLowerCase()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,4 +23,6 @@ export const DEFAULT_LOCALE = SUPPORTED_LOCALES.find(locale => locale.default)?.
|
||||||
|
|
||||||
export const LOCALE_STORAGE_LOCALE_KEY = `${APP_NAME_KEY}-user-locale`;
|
export const LOCALE_STORAGE_LOCALE_KEY = `${APP_NAME_KEY}-user-locale`;
|
||||||
|
|
||||||
export const LOCALE_STORAGE_REFRESH_KEY = `${APP_NAME_KEY}-refresh`;
|
export const LOCALE_STORAGE_REFRESH_KEY = `${APP_NAME_KEY}-refresh`;
|
||||||
|
|
||||||
|
export const LOCALE_STORAGE_STAY_LOGIN_KEY = `${APP_NAME_KEY}-remember`;
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import i18n from '@/core/plugins/i18n.config';
|
import i18n from '@/core/plugins/i18n.config';
|
||||||
import {DEFAULT_LOCALE, LOCALE_STORAGE_LOCALE_KEY, SUPPORTED_LOCALES} from "@/config/index.js";
|
import {DEFAULT_LOCALE, LOCALE_STORAGE_LOCALE_KEY, SUPPORTED_LOCALES} from "@/config/index.js";
|
||||||
|
|
||||||
const translation = {
|
const translations = {
|
||||||
get currentLocale() {
|
get currentLocale() {
|
||||||
return i18n.global.locale.value
|
return i18n.global.locale.value
|
||||||
},
|
},
|
||||||
|
|
@ -10,12 +10,28 @@ const translation = {
|
||||||
i18n.global.locale.value = newLocale
|
i18n.global.locale.value = newLocale
|
||||||
},
|
},
|
||||||
|
|
||||||
switchLanguage(newLocale) {
|
switchLanguage(newLocale, router = null, route = null) {
|
||||||
translation.currentLocale = newLocale
|
translations.currentLocale = newLocale
|
||||||
|
|
||||||
document.querySelector('html').setAttribute('lang', newLocale)
|
document.querySelector('html').setAttribute('lang', newLocale)
|
||||||
|
|
||||||
localStorage.setItem(LOCALE_STORAGE_LOCALE_KEY, newLocale)
|
localStorage.setItem(LOCALE_STORAGE_LOCALE_KEY, newLocale)
|
||||||
|
|
||||||
|
if (router && route) {
|
||||||
|
const newRoute = {
|
||||||
|
...route,
|
||||||
|
params: {
|
||||||
|
...route.params,
|
||||||
|
locale: newLocale
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
router.push(newRoute).catch(err => {
|
||||||
|
if (err.name !== 'NavigationDuplicated') {
|
||||||
|
console.error('Navigation error:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isLocaleSupported(locale) {
|
isLocaleSupported(locale) {
|
||||||
|
|
@ -39,7 +55,7 @@ const translation = {
|
||||||
getPersistedLocale() {
|
getPersistedLocale() {
|
||||||
const persistedLocale = localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY)
|
const persistedLocale = localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY)
|
||||||
|
|
||||||
if (translation.isLocaleSupported(persistedLocale)) {
|
if (translations.isLocaleSupported(persistedLocale)) {
|
||||||
return persistedLocale
|
return persistedLocale
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
|
|
@ -47,18 +63,18 @@ const translation = {
|
||||||
},
|
},
|
||||||
|
|
||||||
guessDefaultLocale() {
|
guessDefaultLocale() {
|
||||||
const userPersistedLocale = translation.getPersistedLocale()
|
const userPersistedLocale = translations.getPersistedLocale()
|
||||||
if (userPersistedLocale) {
|
if (userPersistedLocale) {
|
||||||
return userPersistedLocale
|
return userPersistedLocale
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPreferredLocale = translation.getUserLocale()
|
const userPreferredLocale = translations.getUserLocale()
|
||||||
|
|
||||||
if (translation.isLocaleSupported(userPreferredLocale.locale)) {
|
if (translations.isLocaleSupported(userPreferredLocale.locale)) {
|
||||||
return userPreferredLocale.locale
|
return userPreferredLocale.locale
|
||||||
}
|
}
|
||||||
|
|
||||||
if (translation.isLocaleSupported(userPreferredLocale.localeNoRegion)) {
|
if (translations.isLocaleSupported(userPreferredLocale.localeNoRegion)) {
|
||||||
return userPreferredLocale.localeNoRegion
|
return userPreferredLocale.localeNoRegion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,11 +84,11 @@ const translation = {
|
||||||
async routeMiddleware(to, _from, next) {
|
async routeMiddleware(to, _from, next) {
|
||||||
const paramLocale = to.params.locale
|
const paramLocale = to.params.locale
|
||||||
|
|
||||||
if (!translation.isLocaleSupported(paramLocale)) {
|
if (!translations.isLocaleSupported(paramLocale)) {
|
||||||
return next(translation.guessDefaultLocale())
|
return next(translations.guessDefaultLocale())
|
||||||
}
|
}
|
||||||
|
|
||||||
await translation.switchLanguage(paramLocale)
|
await translations.switchLanguage(paramLocale)
|
||||||
|
|
||||||
return next()
|
return next()
|
||||||
},
|
},
|
||||||
|
|
@ -81,11 +97,11 @@ const translation = {
|
||||||
return {
|
return {
|
||||||
...to,
|
...to,
|
||||||
params: {
|
params: {
|
||||||
locale: translation.currentLocale,
|
locale: translations.currentLocale,
|
||||||
...to.params
|
...to.params
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default translation
|
export default translations
|
||||||
|
|
|
||||||
11
storefront/src/graphql/fragments/categories.fragment.js
Normal file
11
storefront/src/graphql/fragments/categories.fragment.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const CATEGORY_FRAGMENT = gql`
|
||||||
|
fragment Category on CategoryType {
|
||||||
|
name
|
||||||
|
uuid
|
||||||
|
image
|
||||||
|
description
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
`
|
||||||
12
storefront/src/graphql/fragments/company.fragment.js
Normal file
12
storefront/src/graphql/fragments/company.fragment.js
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const COMPANY_FRAGMENT = gql`
|
||||||
|
fragment Company on ConfigType {
|
||||||
|
companyAddress
|
||||||
|
companyName
|
||||||
|
companyPhoneNumber
|
||||||
|
emailFrom
|
||||||
|
emailHostUser
|
||||||
|
projectName
|
||||||
|
}
|
||||||
|
`
|
||||||
9
storefront/src/graphql/fragments/languages.fragment.js
Normal file
9
storefront/src/graphql/fragments/languages.fragment.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const LANGUAGES_FRAGMENT = gql`
|
||||||
|
fragment Languages on LanguageType {
|
||||||
|
code
|
||||||
|
flag
|
||||||
|
name
|
||||||
|
}
|
||||||
|
`
|
||||||
28
storefront/src/graphql/fragments/orders.fragment.js
Normal file
28
storefront/src/graphql/fragments/orders.fragment.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import {PRODUCT_FRAGMENT} from "@/graphql/fragments/products.fragment.js";
|
||||||
|
|
||||||
|
export const ORDER_FRAGMENT = gql`
|
||||||
|
fragment Order on OrderType {
|
||||||
|
totalPrice
|
||||||
|
uuid
|
||||||
|
status
|
||||||
|
buyTime
|
||||||
|
totalPrice
|
||||||
|
humanReadableId
|
||||||
|
orderProducts {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
uuid
|
||||||
|
notifications
|
||||||
|
attributes
|
||||||
|
quantity
|
||||||
|
status
|
||||||
|
product {
|
||||||
|
...Product
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${PRODUCT_FRAGMENT}
|
||||||
|
`
|
||||||
37
storefront/src/graphql/fragments/products.fragment.js
Normal file
37
storefront/src/graphql/fragments/products.fragment.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const PRODUCT_FRAGMENT = gql`
|
||||||
|
fragment Product on ProductType {
|
||||||
|
uuid
|
||||||
|
name
|
||||||
|
price
|
||||||
|
quantity
|
||||||
|
slug
|
||||||
|
category {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
images {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributeGroups {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
name
|
||||||
|
uuid
|
||||||
|
attributes {
|
||||||
|
name
|
||||||
|
uuid
|
||||||
|
values {
|
||||||
|
value
|
||||||
|
uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
17
storefront/src/graphql/fragments/user.fragment.js
Normal file
17
storefront/src/graphql/fragments/user.fragment.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const USER_FRAGMENT = gql`
|
||||||
|
fragment User on UserType {
|
||||||
|
avatar
|
||||||
|
uuid
|
||||||
|
attributes
|
||||||
|
language
|
||||||
|
email
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
phoneNumber
|
||||||
|
balance {
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
16
storefront/src/graphql/fragments/wishlist.fragment.js
Normal file
16
storefront/src/graphql/fragments/wishlist.fragment.js
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import {PRODUCT_FRAGMENT} from "@/graphql/fragments/products.fragment.js";
|
||||||
|
|
||||||
|
export const WISHLIST_FRAGMENT = gql`
|
||||||
|
fragment Wishlist on WishlistType {
|
||||||
|
uuid
|
||||||
|
products {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...Product
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${PRODUCT_FRAGMENT}
|
||||||
|
`
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {USER_FRAGMENT} from "@/graphql/fragments/user.fragment.js";
|
||||||
|
|
||||||
export const REGISTER = gql`
|
export const REGISTER = gql`
|
||||||
mutation register(
|
mutation register(
|
||||||
|
|
@ -34,20 +35,11 @@ export const LOGIN = gql`
|
||||||
accessToken
|
accessToken
|
||||||
refreshToken
|
refreshToken
|
||||||
user {
|
user {
|
||||||
avatar
|
...User
|
||||||
uuid
|
|
||||||
attributes
|
|
||||||
language
|
|
||||||
email
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
phoneNumber
|
|
||||||
balance {
|
|
||||||
amount
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${USER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const REFRESH = gql`
|
export const REFRESH = gql`
|
||||||
|
|
@ -60,68 +52,11 @@ export const REFRESH = gql`
|
||||||
accessToken
|
accessToken
|
||||||
refreshToken
|
refreshToken
|
||||||
user {
|
user {
|
||||||
avatar
|
...User
|
||||||
uuid
|
|
||||||
attributes
|
|
||||||
language
|
|
||||||
email
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
phoneNumber
|
|
||||||
balance {
|
|
||||||
amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const ACTIVATE_USER = gql`
|
|
||||||
mutation activateUser(
|
|
||||||
$token: String!,
|
|
||||||
$uid: String!
|
|
||||||
) {
|
|
||||||
activateUser(
|
|
||||||
token: $token,
|
|
||||||
uid: $uid
|
|
||||||
) {
|
|
||||||
success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const UPDATE_USER = gql`
|
|
||||||
mutation updateUser(
|
|
||||||
$firstName: String,
|
|
||||||
$lastName: String,
|
|
||||||
$email: String,
|
|
||||||
$phoneNumber: String,
|
|
||||||
$password: String,
|
|
||||||
$confirmPassword: String,
|
|
||||||
) {
|
|
||||||
updateUser(
|
|
||||||
firstName: $firstName,
|
|
||||||
lastName: $lastName,
|
|
||||||
email: $email,
|
|
||||||
phoneNumber: $phoneNumber,
|
|
||||||
password: $password,
|
|
||||||
confirmPassword: $confirmPassword,
|
|
||||||
) {
|
|
||||||
user {
|
|
||||||
avatar
|
|
||||||
uuid
|
|
||||||
attributes
|
|
||||||
language
|
|
||||||
email
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
phoneNumber
|
|
||||||
balance {
|
|
||||||
amount
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${USER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const RESET_PASSWORD = gql`
|
export const RESET_PASSWORD = gql`
|
||||||
|
|
@ -136,7 +71,7 @@ export const RESET_PASSWORD = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const CONFIRM_RESET_PASSWORD = gql`
|
export const NEW_PASSWORD = gql`
|
||||||
mutation confirmResetPassword(
|
mutation confirmResetPassword(
|
||||||
$password: String!,
|
$password: String!,
|
||||||
$confirmPassword: String!,
|
$confirmPassword: String!,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {ORDER_FRAGMENT} from "@/graphql/fragments/orders.fragment.js";
|
||||||
|
|
||||||
export const ADD_TO_CART = gql`
|
export const ADD_TO_CART = gql`
|
||||||
mutation addToCart(
|
mutation addToCart(
|
||||||
|
|
@ -10,61 +11,11 @@ export const ADD_TO_CART = gql`
|
||||||
productUuid: $productUuid
|
productUuid: $productUuid
|
||||||
) {
|
) {
|
||||||
order {
|
order {
|
||||||
status
|
...Order
|
||||||
uuid
|
|
||||||
totalPrice
|
|
||||||
orderProducts {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
notifications
|
|
||||||
attributes
|
|
||||||
quantity
|
|
||||||
status
|
|
||||||
product {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${ORDER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const REMOVE_FROM_CART = gql`
|
export const REMOVE_FROM_CART = gql`
|
||||||
|
|
@ -77,61 +28,11 @@ export const REMOVE_FROM_CART = gql`
|
||||||
productUuid: $productUuid
|
productUuid: $productUuid
|
||||||
) {
|
) {
|
||||||
order {
|
order {
|
||||||
status
|
...Order
|
||||||
uuid
|
|
||||||
totalPrice
|
|
||||||
orderProducts {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
notifications
|
|
||||||
attributes
|
|
||||||
quantity
|
|
||||||
status
|
|
||||||
product {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${ORDER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const REMOVE_KIND_FROM_CART = gql`
|
export const REMOVE_KIND_FROM_CART = gql`
|
||||||
|
|
@ -144,61 +45,11 @@ export const REMOVE_KIND_FROM_CART = gql`
|
||||||
productUuid: $productUuid
|
productUuid: $productUuid
|
||||||
) {
|
) {
|
||||||
order {
|
order {
|
||||||
status
|
...Order
|
||||||
uuid
|
|
||||||
totalPrice
|
|
||||||
orderProducts {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
notifications
|
|
||||||
attributes
|
|
||||||
quantity
|
|
||||||
status
|
|
||||||
product {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${ORDER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const REMOVE_ALL_FROM_CART = gql`
|
export const REMOVE_ALL_FROM_CART = gql`
|
||||||
|
|
@ -209,59 +60,9 @@ export const REMOVE_ALL_FROM_CART = gql`
|
||||||
orderUuid: $orderUuid
|
orderUuid: $orderUuid
|
||||||
) {
|
) {
|
||||||
order {
|
order {
|
||||||
status
|
...Order
|
||||||
uuid
|
|
||||||
totalPrice
|
|
||||||
orderProducts {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
notifications
|
|
||||||
attributes
|
|
||||||
quantity
|
|
||||||
status
|
|
||||||
product {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${ORDER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -2,14 +2,16 @@ import gql from 'graphql-tag'
|
||||||
|
|
||||||
export const CONTACT_US = gql`
|
export const CONTACT_US = gql`
|
||||||
mutation contactUs(
|
mutation contactUs(
|
||||||
$email: String!,
|
|
||||||
$name: String!,
|
$name: String!,
|
||||||
|
$email: String!,
|
||||||
|
$phoneNumber: String,
|
||||||
$subject: String!,
|
$subject: String!,
|
||||||
$message: String!,
|
$message: String!,
|
||||||
) {
|
) {
|
||||||
contactUs(
|
contactUs(
|
||||||
email: $email,
|
|
||||||
name: $name,
|
name: $name,
|
||||||
|
email: $email,
|
||||||
|
phoneNumber: $phoneNumber,
|
||||||
subject: $subject,
|
subject: $subject,
|
||||||
message: $message
|
message: $message
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
17
storefront/src/graphql/mutations/languages.js
Normal file
17
storefront/src/graphql/mutations/languages.js
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import gql from "graphql-tag";
|
||||||
|
|
||||||
|
export const SWITCH_LANGUAGE = gql`
|
||||||
|
mutation setlanguage(
|
||||||
|
$uuid: UUID!,
|
||||||
|
$language: String,
|
||||||
|
) {
|
||||||
|
updateUser(
|
||||||
|
uuid: $uuid,
|
||||||
|
language: $language
|
||||||
|
) {
|
||||||
|
user {
|
||||||
|
...User
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
41
storefront/src/graphql/mutations/user.js
Normal file
41
storefront/src/graphql/mutations/user.js
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const ACTIVATE_USER = gql`
|
||||||
|
mutation activateUser(
|
||||||
|
$token: String!,
|
||||||
|
$uid: String!
|
||||||
|
) {
|
||||||
|
activateUser(
|
||||||
|
token: $token,
|
||||||
|
uid: $uid
|
||||||
|
) {
|
||||||
|
success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const UPDATE_USER = gql`
|
||||||
|
mutation updateUser(
|
||||||
|
$uuid: UUID!,
|
||||||
|
$firstName: String,
|
||||||
|
$lastName: String,
|
||||||
|
$email: String,
|
||||||
|
$phoneNumber: String,
|
||||||
|
$password: String,
|
||||||
|
$confirmPassword: String,
|
||||||
|
) {
|
||||||
|
updateUser(
|
||||||
|
uuid: $uuid,
|
||||||
|
firstName: $firstName,
|
||||||
|
lastName: $lastName,
|
||||||
|
email: $email,
|
||||||
|
phoneNumber: $phoneNumber,
|
||||||
|
password: $password,
|
||||||
|
confirmPassword: $confirmPassword,
|
||||||
|
) {
|
||||||
|
user {
|
||||||
|
...User
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {WISHLIST_FRAGMENT} from "@/graphql/fragments/wishlist.fragment.js";
|
||||||
|
|
||||||
export const ADD_TO_WISHLIST = gql`
|
export const ADD_TO_WISHLIST = gql`
|
||||||
mutation addToWishlist(
|
mutation addToWishlist(
|
||||||
|
|
@ -10,30 +11,11 @@ export const ADD_TO_WISHLIST = gql`
|
||||||
productUuid: $productUuid
|
productUuid: $productUuid
|
||||||
) {
|
) {
|
||||||
wishlist {
|
wishlist {
|
||||||
uuid
|
...Wishlist
|
||||||
products {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${WISHLIST_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const REMOVE_FROM_WISHLIST = gql`
|
export const REMOVE_FROM_WISHLIST = gql`
|
||||||
|
|
@ -46,93 +28,24 @@ export const REMOVE_FROM_WISHLIST = gql`
|
||||||
productUuid: $productUuid
|
productUuid: $productUuid
|
||||||
) {
|
) {
|
||||||
wishlist {
|
wishlist {
|
||||||
uuid
|
...Wishlist
|
||||||
products {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${WISHLIST_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const REMOVE_ALL_FROM_WISHLIST = gql`
|
export const REMOVE_ALL_FROM_WISHLIST = gql`
|
||||||
mutation removeAllFromCart(
|
mutation removeAllFromWishlist(
|
||||||
$wishlistUuid: String!
|
$wishlistUuid: String!
|
||||||
) {
|
) {
|
||||||
removeAllWishlistProducts(
|
removeAllWishlistProducts(
|
||||||
wishlistUuid: $wishlistUuid
|
wishlistUuid: $wishlistUuid
|
||||||
) {
|
) {
|
||||||
order {
|
wishlist {
|
||||||
status
|
...Wishlist
|
||||||
uuid
|
|
||||||
totalPrice
|
|
||||||
orderProducts {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
notifications
|
|
||||||
attributes
|
|
||||||
quantity
|
|
||||||
status
|
|
||||||
product {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${WISHLIST_FRAGMENT}
|
||||||
`
|
`
|
||||||
29
storefront/src/graphql/queries/blog.js
Normal file
29
storefront/src/graphql/queries/blog.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const GET_POSTS = gql`
|
||||||
|
query getPosts {
|
||||||
|
posts {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const GET_POST_BY_SLUG = gql`
|
||||||
|
query getPostBySlug(
|
||||||
|
$slug: String!
|
||||||
|
) {
|
||||||
|
posts(
|
||||||
|
slug: $slug
|
||||||
|
) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
@ -1,19 +1,17 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {CATEGORY_FRAGMENT} from "@/graphql/fragments/categories.fragment.js";
|
||||||
|
|
||||||
export const GET_CATEGORIES = gql`
|
export const GET_CATEGORIES = gql`
|
||||||
query getCategories {
|
query getCategories {
|
||||||
categories {
|
categories {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
name
|
...Category
|
||||||
uuid
|
|
||||||
image
|
|
||||||
description
|
|
||||||
slug
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${CATEGORY_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const GET_CATEGORY_BY_SLUG = gql`
|
export const GET_CATEGORY_BY_SLUG = gql`
|
||||||
|
|
@ -25,11 +23,7 @@ export const GET_CATEGORY_BY_SLUG = gql`
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
name
|
...Category
|
||||||
uuid
|
|
||||||
image
|
|
||||||
description
|
|
||||||
slug
|
|
||||||
filterableAttributes {
|
filterableAttributes {
|
||||||
possibleValues
|
possibleValues
|
||||||
attributeName
|
attributeName
|
||||||
|
|
@ -42,4 +36,5 @@ export const GET_CATEGORY_BY_SLUG = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${CATEGORY_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {COMPANY_FRAGMENT} from "@/graphql/fragments/company.fragment.js";
|
||||||
|
|
||||||
export const GET_COMPANY_INFO = gql`
|
export const GET_COMPANY_INFO = gql`
|
||||||
query getCompanyInfo {
|
query getCompanyInfo {
|
||||||
parameters {
|
parameters {
|
||||||
companyAddress
|
...Company
|
||||||
companyName
|
|
||||||
companyPhoneNumber
|
|
||||||
emailFrom
|
|
||||||
emailHostUser
|
|
||||||
projectName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${COMPANY_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import gql from 'graphql-tag'
|
|
||||||
|
|
||||||
export const GET_DOCS = gql`
|
|
||||||
query getDocs(
|
|
||||||
$slug: String!
|
|
||||||
) {
|
|
||||||
posts(
|
|
||||||
slug: $slug
|
|
||||||
) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {LANGUAGES_FRAGMENT} from "@/graphql/fragments/languages.fragment.js";
|
||||||
|
|
||||||
export const GET_LANGUAGES = gql`
|
export const GET_LANGUAGES = gql`
|
||||||
query getLanguages {
|
query getLanguages {
|
||||||
languages {
|
languages {
|
||||||
code
|
...Languages
|
||||||
flag
|
|
||||||
name
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${LANGUAGES_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {ORDER_FRAGMENT} from "@/graphql/fragments/orders.fragment.js";
|
||||||
|
|
||||||
export const GET_ORDERS = gql`
|
export const GET_ORDERS = gql`
|
||||||
query getOrders(
|
query getOrders(
|
||||||
|
|
@ -12,63 +13,10 @@ export const GET_ORDERS = gql`
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
totalPrice
|
...Order
|
||||||
uuid
|
|
||||||
status
|
|
||||||
buyTime
|
|
||||||
totalPrice
|
|
||||||
humanReadableId
|
|
||||||
orderProducts {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
notifications
|
|
||||||
attributes
|
|
||||||
quantity
|
|
||||||
status
|
|
||||||
product {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${ORDER_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {PRODUCT_FRAGMENT} from "@/graphql/fragments/products.fragment.js";
|
||||||
|
|
||||||
export const GET_PRODUCTS = gql`
|
export const GET_PRODUCTS = gql`
|
||||||
query getProducts(
|
query getProducts(
|
||||||
$after: String,
|
$after: String,
|
||||||
$first: Number,
|
$first: Int,
|
||||||
$categorySlugs: String,
|
$categorySlugs: String,
|
||||||
$orderBy: String,
|
$orderBy: String,
|
||||||
$minPrice: String,
|
$minPrice: Decimal,
|
||||||
$maxPrice: String,
|
$maxPrice: Decimal,
|
||||||
$name: String
|
$name: String
|
||||||
) {
|
) {
|
||||||
products(
|
products(
|
||||||
|
|
@ -22,34 +23,7 @@ export const GET_PRODUCTS = gql`
|
||||||
edges {
|
edges {
|
||||||
cursor
|
cursor
|
||||||
node {
|
node {
|
||||||
uuid
|
...Product
|
||||||
name
|
|
||||||
price
|
|
||||||
quantity
|
|
||||||
slug
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pageInfo {
|
pageInfo {
|
||||||
|
|
@ -58,6 +32,7 @@ export const GET_PRODUCTS = gql`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${PRODUCT_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const GET_PRODUCT_BY_SLUG = gql`
|
export const GET_PRODUCT_BY_SLUG = gql`
|
||||||
|
|
@ -69,48 +44,10 @@ export const GET_PRODUCT_BY_SLUG = gql`
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
uuid
|
...Product
|
||||||
name
|
|
||||||
price
|
|
||||||
quantity
|
|
||||||
description
|
|
||||||
slug
|
|
||||||
category {
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
feedbacks {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
rating
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${PRODUCT_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -1,48 +1,15 @@
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
import {WISHLIST_FRAGMENT} from "@/graphql/fragments/wishlist.fragment.js";
|
||||||
|
|
||||||
export const GET_WISHLIST = gql`
|
export const GET_WISHLIST = gql`
|
||||||
query getWishlist {
|
query getWishlist {
|
||||||
wishlists {
|
wishlists {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
uuid
|
...Wishlist
|
||||||
products {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
price
|
|
||||||
name
|
|
||||||
description
|
|
||||||
slug
|
|
||||||
images {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
uuid
|
|
||||||
image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
attributeGroups {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
attributes {
|
|
||||||
name
|
|
||||||
uuid
|
|
||||||
values {
|
|
||||||
value
|
|
||||||
uuid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${WISHLIST_FRAGMENT}
|
||||||
`
|
`
|
||||||
|
|
@ -7,7 +7,9 @@
|
||||||
"goEmail": "Take me to my inbox",
|
"goEmail": "Take me to my inbox",
|
||||||
"logout": "Log Out",
|
"logout": "Log Out",
|
||||||
"buy": "Buy Now",
|
"buy": "Buy Now",
|
||||||
"save": "Save"
|
"save": "Save",
|
||||||
|
"sendLink": "Send link",
|
||||||
|
"topUp": "Top up"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"required": "This field is required!",
|
"required": "This field is required!",
|
||||||
|
|
@ -22,36 +24,42 @@
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"search": "Search Cards",
|
"search": "Search Cards",
|
||||||
|
"name": "Name",
|
||||||
"firstName": "First name",
|
"firstName": "First name",
|
||||||
"lastName": "Last name",
|
"lastName": "Last name",
|
||||||
"phone": "Phone number",
|
"phoneNumber": "Phone number",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
|
"subject": "Subject",
|
||||||
"message": "Your message",
|
"message": "Your message",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"newPassword": "New password",
|
"newPassword": "New password",
|
||||||
"confirmPassword": "Confirm password",
|
"confirmPassword": "Confirm password",
|
||||||
"confirmNewPassword": "Confirm new password"
|
"confirmNewPassword": "Confirm new password"
|
||||||
},
|
},
|
||||||
|
"checkboxes": {
|
||||||
|
"remember": "Remember me"
|
||||||
|
},
|
||||||
"popup": {
|
"popup": {
|
||||||
"error": "Error!",
|
"errors": {
|
||||||
"login": {
|
"main": "Error!",
|
||||||
"title": "Wellcome back!",
|
"defaultError": "Something went wrong..",
|
||||||
"text": "Sign in successes"
|
"noDataToUpdate": "There is no data to update."
|
||||||
},
|
},
|
||||||
"register": {
|
"success": {
|
||||||
"title": "Welcome!",
|
"login": "Sign in successes",
|
||||||
"text": "Account successfully created. Please confirm your Email before Sign In!"
|
"register": "Account successfully created. Please confirm your Email before Sign In!",
|
||||||
|
"confirmEmail": "Verification E-mail link successfully sent!",
|
||||||
|
"reset": "If specified email exists in our system, we will send a password recovery email!",
|
||||||
|
"newPassword": "You have successfully changed your password!",
|
||||||
|
"contactUs": "Your message was sent successfully!"
|
||||||
},
|
},
|
||||||
"addToCart": "{product} has been added to the cart!",
|
"addToCart": "{product} has been added to the cart!",
|
||||||
"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",
|
||||||
"contactSuccess": "Your message was sent successfully!",
|
|
||||||
"activationSuccess": "E-mail successfully verified. Please Sign In!",
|
"activationSuccess": "E-mail successfully verified. Please Sign In!",
|
||||||
"successUpdate": "Profile successfully updated!",
|
"successUpdate": "Profile successfully updated!",
|
||||||
"confirmEmail": "Verification E-mail link successfully sent!",
|
|
||||||
"payment": "Your purchase is being processed! Please stand by",
|
"payment": "Your purchase is being processed! Please stand by",
|
||||||
"reset": "If specified email exists in our system, we will send a password recovery email!",
|
|
||||||
"successNewPassword": "You have successfully changed your password!",
|
|
||||||
"successCheckout": "Order purchase successful!",
|
"successCheckout": "Order purchase successful!",
|
||||||
"addToWishlist": "{product} has been added to the wishlist!"
|
"addToWishlist": "{product} has been added to the wishlist!"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
storefront/src/pages/blog-page.vue
Normal file
24
storefront/src/pages/blog-page.vue
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
<template>
|
||||||
|
<div class="blog">
|
||||||
|
<div class="container">
|
||||||
|
<div class="blog__wrapper">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {usePosts} from "@/composables/blog";
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
|
||||||
|
const { posts, loading, getPosts } = usePosts();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getPosts()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -5,7 +5,21 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
import {useUserActivation} from "@/composables/user";
|
||||||
|
import DepositForm from "@/components/forms/deposit-form.vue";
|
||||||
|
import LoginForm from "@/components/forms/login-form.vue";
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { activateUser } = useUserActivation();
|
||||||
|
|
||||||
|
onMounted( async () => {
|
||||||
|
if (route.name === "activate-user") {
|
||||||
|
await activateUser()
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
29
storefront/src/pages/post-page.vue
Normal file
29
storefront/src/pages/post-page.vue
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<div class="post">
|
||||||
|
<div class="container">
|
||||||
|
<div class="post__wrapper">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {computed, onMounted} from "vue";
|
||||||
|
import {usePostbySlug} from "@/composables/blog/usePostBySlug.js";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const slug = computed(() => route.params.postSlug)
|
||||||
|
|
||||||
|
const { post, loading, getPost } = usePostbySlug();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getPost(slug.value)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
29
storefront/src/pages/product-page.vue
Normal file
29
storefront/src/pages/product-page.vue
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<div class="product">
|
||||||
|
<div class="container">
|
||||||
|
<div class="product__wrapper">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {computed, onMounted} from "vue";
|
||||||
|
import {useRoute} from "vue-router";
|
||||||
|
import {useProductbySlug} from "@/composables/products/useProductBySlug.js";
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const slug = computed(() => route.params.productSlug)
|
||||||
|
|
||||||
|
const { product, loading, getProduct } = useProductbySlug();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getProduct(slug.value)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
19
storefront/src/pages/profile-page.vue
Normal file
19
storefront/src/pages/profile-page.vue
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<template>
|
||||||
|
<div class="profile">
|
||||||
|
<div class="container">
|
||||||
|
<div class="profile__wrapper">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.profile {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
28
storefront/src/pages/store-page.vue
Normal file
28
storefront/src/pages/store-page.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<div class="store">
|
||||||
|
<div class="container">
|
||||||
|
<div class="store__wrapper">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {onMounted} from "vue";
|
||||||
|
import {useProducts} from "@/composables/products/index.js";
|
||||||
|
|
||||||
|
const { products, pageInfo, loading, getProducts } = useProducts();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getProducts({})
|
||||||
|
console.log('products:', products)
|
||||||
|
console.log('pageInfo:', pageInfo)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.store {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -2,6 +2,16 @@ import {createRouter, createWebHistory, RouterView} from 'vue-router'
|
||||||
import HomePage from "@/pages/home-page.vue";
|
import HomePage from "@/pages/home-page.vue";
|
||||||
import translation from "@/core/helpers/translations.js";
|
import translation from "@/core/helpers/translations.js";
|
||||||
import {APP_NAME} from "@/config/index.js";
|
import {APP_NAME} from "@/config/index.js";
|
||||||
|
import NewPasswordForm from "@/components/forms/new-password-form.vue";
|
||||||
|
import BlogPage from "@/pages/blog-page.vue";
|
||||||
|
import PostPage from "@/pages/post-page.vue";
|
||||||
|
import ProfilePage from "@/pages/profile-page.vue";
|
||||||
|
import {useAuthStore} from "@/stores/auth.js";
|
||||||
|
import RegisterForm from "@/components/forms/register-form.vue";
|
||||||
|
import LoginForm from "@/components/forms/login-form.vue";
|
||||||
|
import ResetPasswordForm from "@/components/forms/reset-password-form.vue";
|
||||||
|
import StorePage from "@/pages/store-page.vue";
|
||||||
|
import ProductPage from "@/pages/product-page.vue";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
|
@ -16,6 +26,90 @@ const routes = [
|
||||||
meta: {
|
meta: {
|
||||||
title: "Home"
|
title: "Home"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'activate-user',
|
||||||
|
name: 'activate-user',
|
||||||
|
component: HomePage,
|
||||||
|
meta: {
|
||||||
|
title: 'Home'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'reset-password',
|
||||||
|
name: 'reset-password',
|
||||||
|
component: NewPasswordForm,
|
||||||
|
meta: {
|
||||||
|
title: 'New Password'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'register',
|
||||||
|
name: 'register',
|
||||||
|
component: RegisterForm,
|
||||||
|
meta: {
|
||||||
|
title: 'Register',
|
||||||
|
requiresGuest: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'login',
|
||||||
|
name: 'login',
|
||||||
|
component: LoginForm,
|
||||||
|
meta: {
|
||||||
|
title: 'Login',
|
||||||
|
requiresGuest: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'forgot-password',
|
||||||
|
name: 'forgot-password',
|
||||||
|
component: ResetPasswordForm,
|
||||||
|
meta: {
|
||||||
|
title: 'Forgot Password',
|
||||||
|
requiresGuest: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'blog',
|
||||||
|
name: 'blog',
|
||||||
|
component: BlogPage,
|
||||||
|
meta: {
|
||||||
|
title: 'Blog'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'blog/post/:postSlug',
|
||||||
|
name: 'blog-post',
|
||||||
|
component: PostPage,
|
||||||
|
meta: {
|
||||||
|
title: 'Post'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'store',
|
||||||
|
name: 'store',
|
||||||
|
component: StorePage,
|
||||||
|
meta: {
|
||||||
|
title: 'Store'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'product/:productSlug',
|
||||||
|
name: 'product',
|
||||||
|
component: ProductPage,
|
||||||
|
meta: {
|
||||||
|
title: 'Product'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'profile',
|
||||||
|
name: 'profile',
|
||||||
|
component: ProfilePage,
|
||||||
|
meta: {
|
||||||
|
title: 'Profile',
|
||||||
|
requiresAuth: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -34,8 +128,24 @@ const router = createRouter({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
router.beforeEach((to, from) => {
|
router.beforeEach((to, from, next) => {
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const isAuthenticated = authStore.accessToken
|
||||||
|
|
||||||
document.title = to.meta.title ? `${APP_NAME} | ` + to.meta?.title : APP_NAME
|
document.title = to.meta.title ? `${APP_NAME} | ` + to.meta?.title : APP_NAME
|
||||||
|
|
||||||
|
if (to.meta.requiresAuth && !isAuthenticated) {
|
||||||
|
return next({
|
||||||
|
name: 'home',
|
||||||
|
query: { redirect: to.fullPath }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to.meta.requiresGuest && isAuthenticated) {
|
||||||
|
return next({ name: 'home' });
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
})
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
@ -2,13 +2,13 @@ import {defineStore} from "pinia";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
const user = ref(null)
|
const user = ref(null);
|
||||||
const accessToken = ref(null)
|
const accessToken = ref(null);
|
||||||
|
|
||||||
const setUser = (payload) => {
|
const setUser = (payload) => {
|
||||||
user.value = payload.user
|
user.value = payload.user
|
||||||
accessToken.value = payload.accessToken
|
accessToken.value = payload.accessToken
|
||||||
}
|
}
|
||||||
|
|
||||||
return { user, accessToken, setUser }
|
return { user, accessToken, setUser }
|
||||||
})
|
})
|
||||||
|
|
@ -2,10 +2,10 @@ import {defineStore} from "pinia";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
|
|
||||||
export const useCartStore = defineStore('cart', () => {
|
export const useCartStore = defineStore('cart', () => {
|
||||||
const currentOrder = ref({})
|
const currentOrder = ref({});
|
||||||
const setCurrentOrders = (order) => {
|
const setCurrentOrders = (order) => {
|
||||||
currentOrder.value = order
|
currentOrder.value = order
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentOrder,
|
currentOrder,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
export const useCompanyStore = defineStore('company', () => {
|
export const useCompanyStore = defineStore('company', () => {
|
||||||
const companyInfo = ref([])
|
const companyInfo = ref([]);
|
||||||
const setCompanyInfo = (payload) => {
|
const setCompanyInfo = (payload) => {
|
||||||
companyInfo.value = payload
|
companyInfo.value = payload
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
companyInfo,
|
companyInfo,
|
||||||
|
|
|
||||||
14
storefront/src/stores/languages.js
Normal file
14
storefront/src/stores/languages.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const useLanguageStore = defineStore('language', () => {
|
||||||
|
const languages = ref([]);
|
||||||
|
const setLanguages = (payload) => {
|
||||||
|
languages.value = payload
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
languages,
|
||||||
|
setLanguages
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -2,10 +2,10 @@ import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
export const useWishlistStore = defineStore('wishlist', () => {
|
export const useWishlistStore = defineStore('wishlist', () => {
|
||||||
const wishlist = ref({})
|
const wishlist = ref({});
|
||||||
const setWishlist = (order) => {
|
const setWishlist = (order) => {
|
||||||
wishlist.value = order
|
wishlist.value = order
|
||||||
}
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wishlist,
|
wishlist,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue