Features: Empty out storefront

This commit is contained in:
Egor Pavlovich Gorbunov 2025-05-31 17:59:42 +03:00
parent 7cf133dccf
commit 9129211f76
103 changed files with 0 additions and 7803 deletions

View file

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>%EVIBES_PROJECT_NAME%</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View file

@ -1,8 +0,0 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

File diff suppressed because it is too large Load diff

View file

@ -1,30 +0,0 @@
{
"name": "evibes-frontend",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@apollo/client": "^3.13.8",
"@vue/apollo-composable": "^4.2.2",
"@vueuse/core": "^13.2.0",
"element-plus": "^2.9.11",
"graphql": "^16.11.0",
"graphql-tag": "^2.12.6",
"pinia": "^3.0.1",
"vue": "^3.5.13",
"vue-i18n": "^11.1.4",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
"sass": "^1.83.0",
"sass-loader": "^16.0.4",
"vite": "^6.2.4",
"vite-plugin-vue-devtools": "^7.7.2"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -1,39 +0,0 @@
<script setup>
import { RouterView } from 'vue-router'
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 { getCompanyInfo } = useCompanyInfo();
const { getLanguages } = useLanguages();
onMounted(async () => {
await refresh()
await getCompanyInfo()
await getLanguages()
setInterval(async () => {
await refresh()
}, 600000)
})
</script>
<template>
<main class="main" id="top">
<base-header />
<RouterView v-slot="{ Component }">
<Transition name="opacity" mode="out-in">
<component :is="Component" />
</Transition>
</RouterView>
<base-footer />
</main>
</template>
<style scoped>
</style>

View file

@ -1,44 +0,0 @@
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import { setContext } from '@apollo/client/link/context'
import {DEFAULT_LOCALE, LOCALE_STORAGE_LOCALE_KEY} from "@/config/index.js";
import {computed} from "vue";
import { useAuthStore } from "@/stores/auth.js";
const httpLink = createHttpLink({
uri: 'https://api.' + import.meta.env.EVIBES_BASE_DOMAIN + '/graphql/',
});
const userLocale = computed(() => {
return localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY)
});
export const createApolloClient = () => {
const authStore = useAuthStore()
const accessToken = computed(() => {
return authStore.accessToken
})
const authLink = setContext((_, { headers }) => {
const baseHeaders = {
...headers,
"Accept-language": userLocale.value ? userLocale.value : DEFAULT_LOCALE,
};
if (accessToken.value) {
baseHeaders["X-EVIBES-AUTH"] = `Bearer ${accessToken.value}`;
}
return { headers: baseHeaders };
})
return new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network',
}
}
})
}

View file

@ -1,5 +0,0 @@
<svg width="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M19.7071 5.70711C20.0976 5.31658 20.0976 4.68342 19.7071 4.29289C19.3166 3.90237 18.6834 3.90237 18.2929 4.29289L14.032 8.55382C13.4365 8.20193 12.7418 8 12 8C9.79086 8 8 9.79086 8 12C8 12.7418 8.20193 13.4365 8.55382 14.032L4.29289 18.2929C3.90237 18.6834 3.90237 19.3166 4.29289 19.7071C4.68342 20.0976 5.31658 20.0976 5.70711 19.7071L9.96803 15.4462C10.5635 15.7981 11.2582 16 12 16C14.2091 16 16 14.2091 16 12C16 11.2582 15.7981 10.5635 15.4462 9.96803L19.7071 5.70711ZM12.518 10.0677C12.3528 10.0236 12.1792 10 12 10C10.8954 10 10 10.8954 10 12C10 12.1792 10.0236 12.3528 10.0677 12.518L12.518 10.0677ZM11.482 13.9323L13.9323 11.482C13.9764 11.6472 14 11.8208 14 12C14 13.1046 13.1046 14 12 14C11.8208 14 11.6472 13.9764 11.482 13.9323ZM15.7651 4.8207C14.6287 4.32049 13.3675 4 12 4C9.14754 4 6.75717 5.39462 4.99812 6.90595C3.23268 8.42276 2.00757 10.1376 1.46387 10.9698C1.05306 11.5985 1.05306 12.4015 1.46387 13.0302C1.92276 13.7326 2.86706 15.0637 4.21194 16.3739L5.62626 14.9596C4.4555 13.8229 3.61144 12.6531 3.18002 12C3.6904 11.2274 4.77832 9.73158 6.30147 8.42294C7.87402 7.07185 9.81574 6 12 6C12.7719 6 13.5135 6.13385 14.2193 6.36658L15.7651 4.8207ZM12 18C11.2282 18 10.4866 17.8661 9.78083 17.6334L8.23496 19.1793C9.37136 19.6795 10.6326 20 12 20C14.8525 20 17.2429 18.6054 19.002 17.0941C20.7674 15.5772 21.9925 13.8624 22.5362 13.0302C22.947 12.4015 22.947 11.5985 22.5362 10.9698C22.0773 10.2674 21.133 8.93627 19.7881 7.62611L18.3738 9.04043C19.5446 10.1771 20.3887 11.3469 20.8201 12C20.3097 12.7726 19.2218 14.2684 17.6986 15.5771C16.1261 16.9282 14.1843 18 12 18Z"
fill="#000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,3 +0,0 @@
<svg width="24" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" fill="none">
<path fill="#000000" fill-rule="evenodd" d="M3.415 10.242c-.067-.086-.13-.167-.186-.242a16.806 16.806 0 011.803-2.025C6.429 6.648 8.187 5.5 10 5.5c1.813 0 3.57 1.148 4.968 2.475A16.816 16.816 0 0116.771 10a16.9 16.9 0 01-1.803 2.025C13.57 13.352 11.813 14.5 10 14.5c-1.813 0-3.57-1.148-4.968-2.475a16.799 16.799 0 01-1.617-1.783zm15.423-.788L18 10l.838.546-.002.003-.003.004-.01.016-.037.054a17.123 17.123 0 01-.628.854 18.805 18.805 0 01-1.812 1.998C14.848 14.898 12.606 16.5 10 16.5s-4.848-1.602-6.346-3.025a18.806 18.806 0 01-2.44-2.852 6.01 6.01 0 01-.037-.054l-.01-.016-.003-.004-.001-.002c0-.001-.001-.001.837-.547l-.838-.546.002-.003.003-.004.01-.016a6.84 6.84 0 01.17-.245 18.804 18.804 0 012.308-2.66C5.151 5.1 7.394 3.499 10 3.499s4.848 1.602 6.346 3.025a18.803 18.803 0 012.44 2.852l.037.054.01.016.003.004.001.002zM18 10l.838-.546.355.546-.355.546L18 10zM1.162 9.454L2 10l-.838.546L.807 10l.355-.546zM9 10a1 1 0 112 0 1 1 0 01-2 0zm1-3a3 3 0 100 6 3 3 0 000-6z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,7 +0,0 @@
@mixin hover {
@media (hover: hover) and (pointer: fine) {
&:hover {
@content;
}
}
}

View file

@ -1,5 +0,0 @@
$font_default: '', sans-serif;
$white: #ffffff;
$black: #000000;
$error: #f13838;

View file

@ -1,4 +0,0 @@
@use "modules/normalize";
@use "modules/transitions";
@use "global/mixins";
@use "global/variables";

View file

@ -1,49 +0,0 @@
@use "../global/variables" as *;
* {
margin: 0;
padding: 0;
border: none;
box-sizing: border-box;
}
html {
overflow-x: hidden;
font-family: $font_default;
}
#app {
overflow-x: hidden;
position: relative;
}
a {
text-decoration: none;
color: inherit;
}
input, textarea, button {
font-family: $font_default;
outline: none;
}
button:focus-visible {
outline: none;
}
.container {
max-width: 1500px;
margin-inline: auto;
}
@media (max-width: 1680px) {
.container {
max-width: 1200px;
}
}
@media (max-width: 1300px) {
.container {
width: 90%;
}
}

View file

@ -1,28 +0,0 @@
.opacity-enter-active,
.opacity-leave-active {
transition: 0.3s ease all;
}
.opacity-enter-from,
.opacity-leave-to {
opacity: 0;
}
.fromTop-enter-active,
.fromTop-leave-active {
transition: all 0.3s ease;
}
.fromTop-enter-from,
.fromTop-leave-to {
transform: translateY(-3rem);
opacity: 0;
}
.fromLeft-enter-active,
.fromLeft-leave-active {
transition: all 0.3s ease;
}
.fromLeft-enter-from,
.fromLeft-leave-to {
transform: translateX(-3rem);
opacity: 0;
}

View file

@ -1,19 +0,0 @@
<template>
<footer class="footer">
<div class="container">
<div class="footer_wrapper">
</div>
</div>
</footer>
</template>
<script setup>
</script>
<style lang="scss" scoped>
.footer {
}
</style>

View file

@ -1,26 +0,0 @@
<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>

View file

@ -1,88 +0,0 @@
<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>

View file

@ -1,65 +0,0 @@
<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>

View file

@ -1,65 +0,0 @@
<template>
<form @submit.prevent="handleLogin()" class="form">
<ui-input
:type="'email'"
:placeholder="t('fields.email')"
:rules="[isEmail]"
v-model="email"
/>
<ui-input
:type="'password'"
:placeholder="t('fields.password')"
:rules="[required]"
v-model="password"
/>
<ui-checkbox
v-model="isStayLogin"
>
{{ t('checkboxes.remember') }}
</ui-checkbox>
<ui-button
class="form__button"
:isDisabled="!isFormValid"
:isLoading="loading"
>
{{ t('buttons.signIn') }}
</ui-button>
</form>
</template>
<script setup>
import {useI18n} from "vue-i18n";
import {isEmail, 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 UiCheckbox from "@/components/ui/ui-checkbox.vue";
import {useLogin} from "@/composables/auth";
const {t} = useI18n()
const email = ref('')
const password = ref('')
const isStayLogin = ref(false)
const isFormValid = computed(() => {
return (
isEmail(email.value) === true &&
required(password.value) === true
)
})
const { login, loading } = useLogin();
async function handleLogin() {
await login(email.value, password.value, isStayLogin.value);
}
</script>
<style lang="scss" scoped>
.form {
display: flex;
flex-direction: column;
gap: 20px;
}
</style>

View file

@ -1,66 +0,0 @@
<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>

View file

@ -1,102 +0,0 @@
<template>
<form @submit.prevent="handleRegister()" class="form">
<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="'text'"
:placeholder="t('fields.phoneNumber')"
:rules="[required]"
v-model="phoneNumber"
/>
<ui-input
:type="'email'"
:placeholder="t('fields.email')"
:rules="[isEmail]"
v-model="email"
/>
<ui-input
:type="'password'"
:placeholder="t('fields.password')"
:rules="[isPasswordValid]"
v-model="password"
/>
<ui-input
:type="'password'"
:placeholder="t('fields.confirmPassword')"
:rules="[compareStrings]"
v-model="confirmPassword"
/>
<ui-button
class="form__button"
:isDisabled="!isFormValid"
:isLoading="loading"
>
{{ t('buttons.signUp') }}
</ui-button>
</form>
</template>
<script setup>
import {useI18n} from "vue-i18n";
import {isEmail, isPasswordValid, 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 {useRegister} from "@/composables/auth/index.js";
const {t} = useI18n()
const firstName = ref('')
const lastName = ref('')
const phoneNumber = ref('')
const email = ref('')
const password = ref('')
const confirmPassword = ref('')
const compareStrings = (v) => {
if (v === password.value) return true
return t('errors.compare')
}
const isFormValid = computed(() => {
return (
required(firstName.value) === true &&
required(lastName.value) === true &&
required(phoneNumber.value) === true &&
isEmail(email.value) === true &&
isPasswordValid(password.value) === true &&
compareStrings(confirmPassword.value) === true
)
})
const { register, loading } = useRegister();
async function handleRegister() {
await register(
firstName.value,
lastName.value,
phoneNumber.value,
email.value,
password.value,
confirmPassword.value
);
}
</script>
<style lang="scss" scoped>
.form {
display: flex;
flex-direction: column;
gap: 20px;
}
</style>

View file

@ -1,50 +0,0 @@
<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>

View file

@ -1,104 +0,0 @@
<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>

View file

@ -1,61 +0,0 @@
<template>
<button
class="button"
:disabled="isDisabled"
:class="[{active: isLoading}]"
type="submit"
>
<ui-loader class="button__loader" v-if="isLoading" />
<slot v-else />
</button>
</template>
<script setup>
import UiLoader from "@/components/ui/ui-loader.vue";
const props = defineProps({
isDisabled: Boolean,
isLoading: Boolean
})
</script>
<style lang="scss" scoped>
.button {
position: relative;
width: 100%;
cursor: pointer;
flex-shrink: 0;
transition: 0.2s;
border: 1px solid $black;
background-color: $white;
padding-block: 5px;
display: grid;
place-items: center;
z-index: 1;
color: $black;
text-align: center;
font-size: 14px;
font-weight: 700;
&:hover, &.active {
background-color: $black;
color: $white;
}
&:disabled {
cursor: not-allowed;
background-color: rgba($black, 0.5);
color: $black;
}
&:disabled:hover, &.active {
background-color: rgba($black, 0.5);
color: $black;
}
&__loader {
margin-bottom: 10px;
}
}
</style>

View file

@ -1,67 +0,0 @@
<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>

View file

@ -1,144 +0,0 @@
<template>
<div class="block">
<div class="block__inner">
<input
:placeholder="placeholder"
:type="isPasswordVisible"
:value="modelValue"
@input="onInput"
@keydown="numberOnly ? onlyNumbersKeydown($event) : null"
class="block__input"
>
<button
@click.prevent="setPasswordVisible"
class="block__eyes"
v-if="type === 'password'"
>
<img v-if="isPasswordVisible === 'password'" src="@icons/eyeClosed.svg" alt="eye" loading="lazy">
<img v-else src="@icons/eyeOpened.svg" alt="eye" loading="lazy">
</button>
</div>
<p v-if="!validate" class="block__error">{{ errorMessage }}</p>
</div>
</template>
<script setup>
import {ref} from "vue";
const $emit = defineEmits()
const props = defineProps({
type: String,
placeholder: String,
isError: Boolean,
error: String,
modelValue: [String, Number],
rules: Array,
numberOnly: Boolean
})
const isPasswordVisible = ref(props.type)
const setPasswordVisible = () => {
if (isPasswordVisible.value === 'password') {
isPasswordVisible.value = 'text'
return
}
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 errorMessage = ref('')
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
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;
&__inner {
width: 100%;
position: relative;
}
&__input {
width: 100%;
padding: 6px 12px;
border: 1px solid $black;
background-color: $white;
color: #1f1f1f;
font-size: 12px;
font-weight: 400;
line-height: 20px;
&::placeholder {
color: #2B2B2B;
}
}
&__eyes {
cursor: pointer;
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
background-color: transparent;
display: grid;
place-items: center;
}
&__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>

View file

@ -1,59 +0,0 @@
<template>
<div class="loader">
<li class="loader__dots" id="dot-1"></li>
<li class="loader__dots" id="dot-2"></li>
<li class="loader__dots" id="dot-3"></li>
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
.loader {
display: flex;
gap: 0.6em;
list-style: none;
&__dots {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: $white;
}
}
#dot-1 {
animation: loader-1 0.6s infinite ease-in-out;
}
@keyframes loader-1 {
50% {
opacity: 0;
transform: translateY(-0.3em);
}
}
#dot-2 {
animation: loader-2 0.6s 0.3s infinite ease-in-out;
}
@keyframes loader-2 {
50% {
opacity: 0;
transform: translateY(-0.3em);
}
}
#dot-3 {
animation: loader-3 0.6s 0.6s infinite ease-in-out;
}
@keyframes loader-3 {
50% {
opacity: 0;
transform: translateY(-0.3em);
}
}
</style>

View file

@ -1,90 +0,0 @@
<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>

View file

@ -1,6 +0,0 @@
export * from './useLogin';
export * from './useLogout';
export * from './useNewPassword';
export * from './usePasswordReset';
export * from './useRefresh';
export * from './useRegister';

View file

@ -1,96 +0,0 @@
import {useMutation} from "@vue/apollo-composable";
import {LOGIN} from "@/graphql/mutations/auth.js";
import {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 {
DEFAULT_LOCALE,
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() {
const router = useRouter();
const route = useRoute();
const authStore = useAuthStore()
const {t} = useI18n();
const { mutate: loginMutation } = useMutation(LOGIN);
const { getPendingOrder } = usePendingOrder();
const { getWishlist } = useWishlist();
const loading = ref(false);
async function login(
email,
password,
isStayLogin
) {
loading.value = true;
try {
const response = await loginMutation({
email,
password
});
if (isStayLogin) {
localStorage.setItem(LOCALE_STORAGE_STAY_LOGIN_KEY, 'remember')
}
if (response.data?.obtainJwtToken) {
authStore.setUser({
user: response.data.obtainJwtToken.user,
accessToken: response.data.obtainJwtToken.accessToken
});
localStorage.setItem(LOCALE_STORAGE_REFRESH_KEY, response.data.obtainJwtToken.refreshToken)
ElNotification({
message: t('popup.success.login'),
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) {
translations.switchLanguage(response.data.obtainJwtToken.user.language, router, route)
}
await getPendingOrder(response.data.obtainJwtToken.user.email);
await getWishlist();
}
} catch (error) {
console.error("useLogin 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 {
login,
loading
};
}

View file

@ -1,34 +0,0 @@
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
};
}

View file

@ -1,73 +0,0 @@
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
};
}

View file

@ -1,51 +0,0 @@
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
};
}

View file

@ -1,74 +0,0 @@
import {useMutation} from "@vue/apollo-composable";
import {REFRESH} from "@/graphql/mutations/auth.js";
import {computed, ref} from "vue";
import {ElNotification} from "element-plus";
import {useI18n} from "vue-i18n";
import {useAuthStore} from "@/stores/auth.js";
import {LOCALE_STORAGE_REFRESH_KEY} from "@/config/index.js";
import {useRoute, useRouter} from "vue-router";
import translations from "@/core/helpers/translations.js";
import {usePendingOrder} from "@/composables/orders";
import {useWishlist} from "@/composables/wishlist";
export function useRefresh() {
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const {t} = useI18n();
const { mutate: refreshMutation } = useMutation(REFRESH);
const { getPendingOrder } = usePendingOrder();
const { getWishlist } = useWishlist();
const loading = ref(false);
async function refresh() {
loading.value = true;
const refreshToken = computed(() => localStorage.getItem(LOCALE_STORAGE_REFRESH_KEY))
if (!refreshToken.value) return
try {
const response = await refreshMutation({
refreshToken: refreshToken.value
});
if (response.data?.refreshJwtToken) {
authStore.setUser({
user: response.data.refreshJwtToken.user,
accessToken: response.data.refreshJwtToken.accessToken
})
if (response.data.refreshJwtToken.user.language !== translations.currentLocale) {
translations.switchLanguage(response.data.refreshJwtToken.user.language, router, route)
}
localStorage.setItem(LOCALE_STORAGE_REFRESH_KEY, response.data.refreshJwtToken.refreshToken)
await getPendingOrder(response.data.refreshJwtToken.user.email);
await getWishlist();
}
} catch (error) {
console.error("useRefresh 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 {
refresh,
loading
};
}

View file

@ -1,85 +0,0 @@
import {useMutation} from "@vue/apollo-composable";
import {REGISTER} from "@/graphql/mutations/auth.js";
import {h, ref} from "vue";
import {ElNotification} from "element-plus";
import {useI18n} from "vue-i18n";
import {useMailClient} from "@/composables/utils";
export function useRegister() {
const {t} = useI18n();
const { mutate: registerMutation } = useMutation(REGISTER);
const { mailClientUrl, detectMailClient, openMailClient } = useMailClient();
const loading = ref(false);
async function register(
firstName,
lastName,
phoneNumber,
email,
password,
confirmPassword
) {
loading.value = true;
try {
const response = await registerMutation({
firstName,
lastName,
phoneNumber,
email,
password,
confirmPassword
});
if (response.data?.createUser?.success) {
detectMailClient(email);
ElNotification({
message: h('div', [
h('p', t('popup.success.register')),
mailClientUrl.value ? h(
'button',
{
style: {
marginTop: '10px',
padding: '6px 12px',
backgroundColor: '#000000',
color: '#fff',
border: 'none',
cursor: 'pointer',
},
onClick: () => {
openMailClient()
}
},
t('buttons.goEmail')
) : ''
]),
type: 'success'
})
}
} catch (error) {
console.error("useRegister 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 {
register,
loading
};
}

View file

@ -1 +0,0 @@
export * from './usePosts'

View file

@ -1,24 +0,0 @@
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
};
}

View file

@ -1,20 +0,0 @@
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
};
}

View file

@ -1,2 +0,0 @@
export * from './useCategories'
export * from './useCategorybySlug'

View file

@ -1,20 +0,0 @@
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
};
}

View file

@ -1,24 +0,0 @@
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
};
}

View file

@ -1 +0,0 @@
export * from './useCompanyInfo';

View file

@ -1,26 +0,0 @@
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
};
}

View file

@ -1 +0,0 @@
export * from './useContactUs'

View file

@ -1,59 +0,0 @@
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
};
}

View file

@ -1,2 +0,0 @@
export * from './useLanguageSwitch.js'
export * from './useLanguages'

View file

@ -1,61 +0,0 @@
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
};
}

View file

@ -1,33 +0,0 @@
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
};
}

View file

@ -1 +0,0 @@
export * from './usePendingOrder';

View file

@ -1,24 +0,0 @@
import {useMutation} from "@vue/apollo-composable";
import {GET_ORDERS} from "@/graphql/queries/orders.js";
import {useCartStore} from "@/stores/cart.js";
export function usePendingOrder() {
const cartStore = useCartStore()
const { mutate: pendingOrderMutation } = useMutation(GET_ORDERS);
async function getPendingOrder(userEmail) {
const response = await pendingOrderMutation({
status: "PENDING",
userEmail
});
if (!response.errors) {
cartStore.setCurrentOrders(response.data.orders.edges[0].node)
}
}
return {
getPendingOrder
};
}

View file

@ -1 +0,0 @@
export * from './useProducts'

View file

@ -1,24 +0,0 @@
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
};
}

View file

@ -1,53 +0,0 @@
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
};
}

View file

@ -1,3 +0,0 @@
export * from './useUserUpdating';
export * from './useUserActivation';
export * from '../languages/useLanguageSwitch.js';

View file

@ -1,48 +0,0 @@
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
};
}

View file

@ -1,59 +0,0 @@
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
};
}

View file

@ -1,120 +0,0 @@
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
};
}

View file

@ -1 +0,0 @@
export * from './useMainClient';

View file

@ -1,42 +0,0 @@
import { ref } from 'vue';
export function useMailClient() {
const mailClientUrl = ref(null);
const mailClients = {
'gmail.com': 'https://mail.google.com/',
'outlook.com': 'https://outlook.live.com/',
'icloud.com': 'https://www.icloud.com/mail/',
'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) {
mailClientUrl.value = null;
if (!email) return;
const domain = email.split('@')[1];
Object.entries(mailClients).forEach((el) => {
if (domain === el[0]) mailClientUrl.value = el[1];
});
return mailClientUrl.value;
}
function openMailClient() {
if (mailClientUrl.value) {
window.open(mailClientUrl.value);
}
}
return {
mailClientUrl,
detectMailClient,
openMailClient
};
}

View file

@ -1 +0,0 @@
export * from './useWishlist';

View file

@ -1,21 +0,0 @@
import {useMutation} from "@vue/apollo-composable";
import {GET_WISHLIST} from "@/graphql/queries/wishlist.js";
import {useWishlistStore} from "@/stores/wishlist.js";
export function useWishlist() {
const wishlistStore = useWishlistStore()
const { mutate: wishlistMutation } = useMutation(GET_WISHLIST);
async function getWishlist() {
const response = await wishlistMutation();
if (!response.errors) {
wishlistStore.setWishlist(response.data.wishlists.edges[0].node)
}
}
return {
getWishlist
};
}

View file

@ -1,28 +0,0 @@
// APP
export const APP_NAME = import.meta.env.EVIBES_PROJECT_NAME
export const APP_NAME_KEY = APP_NAME.toLowerCase()
// LOCALES
export const SUPPORTED_LOCALES = [
{
code: 'en-gb',
default: true
}
]
export const DEFAULT_LOCALE = SUPPORTED_LOCALES.find(locale => locale.default)?.code || 'en-gb'
// LOCAL STORAGE
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_STAY_LOGIN_KEY = `${APP_NAME_KEY}-remember`;

View file

@ -1,30 +0,0 @@
export async function loadLocaleMessages(locale) {
try {
const messages = await import(`../locales/${locale}.json`)
return messages.default || messages
} catch (error) {
console.error(`Не удалось загрузить локаль: ${locale}`, error)
return {}
}
}
export function getLocaleFilename(localeCode, localesConfig) {
const localeInfo = localesConfig.find(locale => locale.code === localeCode)
return localeInfo?.file || `${localeCode}.json`
}
export async function loadAllLocaleMessages(supportedLocales) {
const messages = {}
for (const locale of supportedLocales) {
try {
const localeMessages = await import(`../../locales/${locale.code}.json`)
messages[locale.code] = localeMessages.default || localeMessages
} catch (error) {
console.error(`Не удалось загрузить локаль: ${locale.code}`, error)
messages[locale.code] = {}
}
}
return messages
}

View file

@ -1,107 +0,0 @@
import i18n from '@/core/plugins/i18n.config';
import {DEFAULT_LOCALE, LOCALE_STORAGE_LOCALE_KEY, SUPPORTED_LOCALES} from "@/config/index.js";
const translations = {
get currentLocale() {
return i18n.global.locale.value
},
set currentLocale(newLocale) {
i18n.global.locale.value = newLocale
},
switchLanguage(newLocale, router = null, route = null) {
translations.currentLocale = newLocale
document.querySelector('html').setAttribute('lang', 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) {
if (locale) {
return SUPPORTED_LOCALES.some(supportedLocale => supportedLocale.code === locale);
}
return false
},
getUserLocale() {
const locale =
window.navigator.language ||
DEFAULT_LOCALE.code
return {
locale: locale,
localeNoRegion: locale.split('-')[0]
}
},
getPersistedLocale() {
const persistedLocale = localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY)
if (translations.isLocaleSupported(persistedLocale)) {
return persistedLocale
} else {
return null
}
},
guessDefaultLocale() {
const userPersistedLocale = translations.getPersistedLocale()
if (userPersistedLocale) {
return userPersistedLocale
}
const userPreferredLocale = translations.getUserLocale()
if (translations.isLocaleSupported(userPreferredLocale.locale)) {
return userPreferredLocale.locale
}
if (translations.isLocaleSupported(userPreferredLocale.localeNoRegion)) {
return userPreferredLocale.localeNoRegion
}
return DEFAULT_LOCALE.code
},
async routeMiddleware(to, _from, next) {
const paramLocale = to.params.locale
if (!translations.isLocaleSupported(paramLocale)) {
return next(translations.guessDefaultLocale())
}
await translations.switchLanguage(paramLocale)
return next()
},
i18nRoute(to) {
return {
...to,
params: {
locale: translations.currentLocale,
...to.params
}
}
}
}
export default translations

View file

@ -1,33 +0,0 @@
import { createI18n } from 'vue-i18n'
import {DEFAULT_LOCALE, LOCALE_STORAGE_LOCALE_KEY, SUPPORTED_LOCALES} from "@/config/index.js";
import {loadAllLocaleMessages} from "@/core/helpers/i18n-utils.js";
const savedLocale = localStorage.getItem(LOCALE_STORAGE_LOCALE_KEY)
const currentLocale = savedLocale && SUPPORTED_LOCALES.some(locale => locale.code === savedLocale)
? savedLocale
: DEFAULT_LOCALE
if (!savedLocale) {
localStorage.setItem(LOCALE_STORAGE_LOCALE_KEY, DEFAULT_LOCALE)
}
const i18n = createI18n({
locale: currentLocale,
fallbackLocale: DEFAULT_LOCALE,
allowComposition: true,
legacy: false,
globalInjection: true,
messages: {}
})
export async function setupI18n() {
const messages = await loadAllLocaleMessages(SUPPORTED_LOCALES)
Object.keys(messages).forEach(locale => {
i18n.global.setLocaleMessage(locale, messages[locale])
})
return i18n
}
export default i18n

View file

@ -1,42 +0,0 @@
import i18n from '@/core/plugins/i18n.config'
const isEmail = (email) => {
if (!email) return required(email);
if (/.+@.+\..+/.test(email)) return true
const { t } = i18n.global
return t('errors.mail')
}
const required = (text) => {
if (text) return true
const { t } = i18n.global
return t('errors.required')
}
const isPasswordValid = (pass) => {
const { t } = i18n.global
if (pass.length < 8) {
return t('errors.needMin')
}
if (!/[a-z]/.test(pass)) {
return t('errors.needLower')
}
if (!/[A-Z]/.test(pass)) {
return t('errors.needUpper')
}
if (!/\d/.test(pass)) {
return t('errors.needNumber')
}
if (!/[#.?!@$%^&*'()_+=:;"'/>.<,|\-]/.test(pass)) {
return t('errors.needSpecial')
}
return true
}
export { required, isEmail, isPasswordValid }

View file

@ -1,11 +0,0 @@
import gql from 'graphql-tag'
export const CATEGORY_FRAGMENT = gql`
fragment Category on CategoryType {
name
uuid
image
description
slug
}
`

View file

@ -1,12 +0,0 @@
import gql from 'graphql-tag'
export const COMPANY_FRAGMENT = gql`
fragment Company on ConfigType {
companyAddress
companyName
companyPhoneNumber
emailFrom
emailHostUser
projectName
}
`

View file

@ -1,9 +0,0 @@
import gql from 'graphql-tag'
export const LANGUAGES_FRAGMENT = gql`
fragment Languages on LanguageType {
code
flag
name
}
`

View file

@ -1,28 +0,0 @@
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}
`

View file

@ -1,37 +0,0 @@
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
}
}
}
}
}
}
`

View file

@ -1,17 +0,0 @@
import gql from 'graphql-tag'
export const USER_FRAGMENT = gql`
fragment User on UserType {
avatar
uuid
attributes
language
email
firstName
lastName
phoneNumber
balance {
amount
}
}
`

View file

@ -1,16 +0,0 @@
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}
`

View file

@ -1,90 +0,0 @@
import gql from 'graphql-tag'
import {USER_FRAGMENT} from "@/graphql/fragments/user.fragment.js";
export const REGISTER = gql`
mutation register(
$firstName: String!,
$lastName: String!,
$email: String!,
$phoneNumber: String!,
$password: String!,
$confirmPassword: String!
) {
createUser(
firstName: $firstName,
lastName: $lastName,
email: $email,
phoneNumber: $phoneNumber,
password: $password,
confirmPassword: $confirmPassword
) {
success
}
}
`
export const LOGIN = gql`
mutation login(
$email: String!,
$password: String!
) {
obtainJwtToken(
email: $email,
password: $password
) {
accessToken
refreshToken
user {
...User
}
}
}
${USER_FRAGMENT}
`
export const REFRESH = gql`
mutation refresh(
$refreshToken: String!
) {
refreshJwtToken(
refreshToken: $refreshToken
) {
accessToken
refreshToken
user {
...User
}
}
}
${USER_FRAGMENT}
`
export const RESET_PASSWORD = gql`
mutation resetPassword(
$email: String!,
) {
resetPassword(
email: $email,
) {
success
}
}
`
export const NEW_PASSWORD = gql`
mutation confirmResetPassword(
$password: String!,
$confirmPassword: String!,
$token: String!,
$uid: String!,
) {
confirmResetPassword(
password: $password,
confirmPassword: $confirmPassword,
token: $token,
uid: $uid
) {
success
}
}
`

View file

@ -1,68 +0,0 @@
import gql from 'graphql-tag'
import {ORDER_FRAGMENT} from "@/graphql/fragments/orders.fragment.js";
export const ADD_TO_CART = gql`
mutation addToCart(
$orderUuid: String!,
$productUuid: String!
) {
addOrderProduct(
orderUuid: $orderUuid,
productUuid: $productUuid
) {
order {
...Order
}
}
}
${ORDER_FRAGMENT}
`
export const REMOVE_FROM_CART = gql`
mutation removeFromCart(
$orderUuid: String!,
$productUuid: String!
) {
removeOrderProduct(
orderUuid: $orderUuid,
productUuid: $productUuid
) {
order {
...Order
}
}
}
${ORDER_FRAGMENT}
`
export const REMOVE_KIND_FROM_CART = gql`
mutation removeKindFromCart(
$orderUuid: String!,
$productUuid: String!
) {
removeOrderProductsOfAKind(
orderUuid: $orderUuid,
productUuid: $productUuid
) {
order {
...Order
}
}
}
${ORDER_FRAGMENT}
`
export const REMOVE_ALL_FROM_CART = gql`
mutation removeAllFromCart(
$orderUuid: String!
) {
removeAllOrderProducts(
orderUuid: $orderUuid
) {
order {
...Order
}
}
}
${ORDER_FRAGMENT}
`

View file

@ -1,22 +0,0 @@
import gql from 'graphql-tag'
export const CONTACT_US = gql`
mutation contactUs(
$name: String!,
$email: String!,
$phoneNumber: String,
$subject: String!,
$message: String!,
) {
contactUs(
name: $name,
email: $email,
phoneNumber: $phoneNumber,
subject: $subject,
message: $message
) {
error
received
}
}
`

View file

@ -1,14 +0,0 @@
import gql from 'graphql-tag'
export const DEPOSIT = gql`
mutation deposit(
$amount: Number!
) {
contactUs(
amount: $amount,
) {
error
received
}
}
`

View file

@ -1,17 +0,0 @@
import gql from "graphql-tag";
export const SWITCH_LANGUAGE = gql`
mutation setlanguage(
$uuid: UUID!,
$language: String,
) {
updateUser(
uuid: $uuid,
language: $language
) {
user {
...User
}
}
}
`

View file

@ -1,41 +0,0 @@
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
}
}
}
`

View file

@ -1,51 +0,0 @@
import gql from 'graphql-tag'
import {WISHLIST_FRAGMENT} from "@/graphql/fragments/wishlist.fragment.js";
export const ADD_TO_WISHLIST = gql`
mutation addToWishlist(
$wishlistUuid: String!,
$productUuid: String!
) {
addWishlistProduct(
wishlistUuid: $wishlistUuid,
productUuid: $productUuid
) {
wishlist {
...Wishlist
}
}
}
${WISHLIST_FRAGMENT}
`
export const REMOVE_FROM_WISHLIST = gql`
mutation removeFromWishlist(
$wishlistUuid: String!,
$productUuid: String!
) {
removeWishlistProduct(
wishlistUuid: $wishlistUuid,
productUuid: $productUuid
) {
wishlist {
...Wishlist
}
}
}
${WISHLIST_FRAGMENT}
`
export const REMOVE_ALL_FROM_WISHLIST = gql`
mutation removeAllFromWishlist(
$wishlistUuid: String!
) {
removeAllWishlistProducts(
wishlistUuid: $wishlistUuid
) {
wishlist {
...Wishlist
}
}
}
${WISHLIST_FRAGMENT}
`

View file

@ -1,29 +0,0 @@
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
}
}
}
}
`

View file

@ -1,40 +0,0 @@
import gql from 'graphql-tag'
import {CATEGORY_FRAGMENT} from "@/graphql/fragments/categories.fragment.js";
export const GET_CATEGORIES = gql`
query getCategories {
categories {
edges {
node {
...Category
}
}
}
}
${CATEGORY_FRAGMENT}
`
export const GET_CATEGORY_BY_SLUG = gql`
query getCategoryBySlug(
$slug: String!
) {
categories(
slug: $slug
) {
edges {
node {
...Category
filterableAttributes {
possibleValues
attributeName
}
minMaxPrices {
maxPrice
minPrice
}
}
}
}
}
${CATEGORY_FRAGMENT}
`

View file

@ -1,11 +0,0 @@
import gql from 'graphql-tag'
import {COMPANY_FRAGMENT} from "@/graphql/fragments/company.fragment.js";
export const GET_COMPANY_INFO = gql`
query getCompanyInfo {
parameters {
...Company
}
}
${COMPANY_FRAGMENT}
`

View file

@ -1,11 +0,0 @@
import gql from 'graphql-tag'
import {LANGUAGES_FRAGMENT} from "@/graphql/fragments/languages.fragment.js";
export const GET_LANGUAGES = gql`
query getLanguages {
languages {
...Languages
}
}
${LANGUAGES_FRAGMENT}
`

View file

@ -1,22 +0,0 @@
import gql from 'graphql-tag'
import {ORDER_FRAGMENT} from "@/graphql/fragments/orders.fragment.js";
export const GET_ORDERS = gql`
query getOrders(
$status: String!,
$userEmail: String!
) {
orders(
status: $status,
orderBy: "-buyTime",
userEmail: $userEmail
) {
edges {
node {
...Order
}
}
}
}
${ORDER_FRAGMENT}
`

View file

@ -1,53 +0,0 @@
import gql from 'graphql-tag'
import {PRODUCT_FRAGMENT} from "@/graphql/fragments/products.fragment.js";
export const GET_PRODUCTS = gql`
query getProducts(
$after: String,
$first: Int,
$categorySlugs: String,
$orderBy: String,
$minPrice: Decimal,
$maxPrice: Decimal,
$name: String
) {
products(
after: $after,
first: $first,
categorySlugs: $categorySlugs,
orderBy: $orderBy,
minPrice: $minPrice,
maxPrice: $maxPrice,
name: $name
) {
edges {
cursor
node {
...Product
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
${PRODUCT_FRAGMENT}
`
export const GET_PRODUCT_BY_SLUG = gql`
query getProductBySlug(
$slug: String!
) {
products(
slug: $slug
) {
edges {
node {
...Product
}
}
}
}
${PRODUCT_FRAGMENT}
`

View file

@ -1,15 +0,0 @@
import gql from 'graphql-tag'
import {WISHLIST_FRAGMENT} from "@/graphql/fragments/wishlist.fragment.js";
export const GET_WISHLIST = gql`
query getWishlist {
wishlists {
edges {
node {
...Wishlist
}
}
}
}
${WISHLIST_FRAGMENT}
`

View file

@ -1,66 +0,0 @@
{
"buttons": {
"signIn": "Sign In",
"signUp": "Sign Up",
"addToCart": "Add To Cart",
"send": "Send",
"goEmail": "Take me to my inbox",
"logout": "Log Out",
"buy": "Buy Now",
"save": "Save",
"sendLink": "Send link",
"topUp": "Top up"
},
"errors": {
"required": "This field is required!",
"mail": "Email must be valid!",
"compare": "Passwords don't match!",
"needLower": "Please include lowercase letter.",
"needUpper": "Please include uppercase letter.",
"needNumber": "Please include number.",
"needMin": "Min. 8 characters",
"needSpecial": "Please include a special character: #.?!$%^&*'()_+=:;\"'/>.<,|\\-",
"pageNotFound": "Page not found"
},
"fields": {
"search": "Search Cards",
"name": "Name",
"firstName": "First name",
"lastName": "Last name",
"phoneNumber": "Phone number",
"email": "Email",
"subject": "Subject",
"message": "Your message",
"password": "Password",
"newPassword": "New password",
"confirmPassword": "Confirm password",
"confirmNewPassword": "Confirm new password"
},
"checkboxes": {
"remember": "Remember me"
},
"popup": {
"errors": {
"main": "Error!",
"defaultError": "Something went wrong..",
"noDataToUpdate": "There is no data to update."
},
"success": {
"login": "Sign in successes",
"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!",
"addToCartLimit": "Total quantity limit is {quantity}!",
"failAdd": "Please log in to make a purchase",
"activationSuccess": "E-mail successfully verified. Please Sign In!",
"successUpdate": "Profile successfully updated!",
"payment": "Your purchase is being processed! Please stand by",
"successCheckout": "Order purchase successful!",
"addToWishlist": "{product} has been added to the wishlist!"
}
}

View file

@ -1,32 +0,0 @@
import '@/assets/styles/global/fonts.scss'
import '@/assets/styles/main.scss'
import {createApp, h, provide} from 'vue'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { createApolloClient } from './apollo'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import {setupI18n} from "@/core/plugins/i18n.config.js";
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/dark/css-vars.css'
const pinia = createPinia()
const i18n = await setupI18n()
const app = createApp({
setup() {
const apolloClient = createApolloClient()
provide(DefaultApolloClient, apolloClient)
},
render: () => h(App)
})
app
.use(pinia)
.use(i18n)
.use(router)
.use(ElementPlus)
app.mount('#app')

View file

@ -1,24 +0,0 @@
<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>

View file

@ -1,27 +0,0 @@
<template>
<div>
</div>
</template>
<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>
<style lang="scss" scoped>
</style>

View file

@ -1,29 +0,0 @@
<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>

View file

@ -1,29 +0,0 @@
<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>

View file

@ -1,19 +0,0 @@
<template>
<div class="profile">
<div class="container">
<div class="profile__wrapper">
</div>
</div>
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped>
.profile {
}
</style>

View file

@ -1,28 +0,0 @@
<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>

View file

@ -1,151 +0,0 @@
import {createRouter, createWebHistory, RouterView} from 'vue-router'
import HomePage from "@/pages/home-page.vue";
import translation from "@/core/helpers/translations.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 = [
{
path: '/:locale?',
component: RouterView,
beforeEnter: translation.routeMiddleware,
children: [
{
path: '',
name: 'home',
component: HomePage,
meta: {
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
}
}
]
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
scrollBehavior() {
document.querySelector('#top').scrollIntoView({ behavior: 'smooth' })
return {
top: 0,
left: 0,
behavior: 'smooth'
}
}
})
router.beforeEach((to, from, next) => {
const authStore = useAuthStore();
const isAuthenticated = authStore.accessToken
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

View file

@ -1,14 +0,0 @@
import {defineStore} from "pinia";
import {ref} from "vue";
export const useAuthStore = defineStore('auth', () => {
const user = ref(null);
const accessToken = ref(null);
const setUser = (payload) => {
user.value = payload.user
accessToken.value = payload.accessToken
}
return { user, accessToken, setUser }
})

View file

@ -1,14 +0,0 @@
import {defineStore} from "pinia";
import {ref} from "vue";
export const useCartStore = defineStore('cart', () => {
const currentOrder = ref({});
const setCurrentOrders = (order) => {
currentOrder.value = order
};
return {
currentOrder,
setCurrentOrders
}
})

View file

@ -1,14 +0,0 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useCompanyStore = defineStore('company', () => {
const companyInfo = ref([]);
const setCompanyInfo = (payload) => {
companyInfo.value = payload
};
return {
companyInfo,
setCompanyInfo
}
})

Some files were not shown because too many files have changed in this diff Show more