feat(storefront): refactor i18n and cart/wishlist handling for improved user experience

Refactored i18n configuration, replacing `DEFAULT_LOCALE` with `DEFAULT_LOCALE_FALLBACK` and enhancing environment-based locale validation. Improved cookie persistence for cart and wishlist, ensuring fallback handling for unauthenticated users.

Enhancements:
- Added `createProjectKey` utility for consistent project key generation.
- Reworked cart and wishlist composables (`useOrderOverwrite`, `useWishlistOverwrite`) to decouple product identifier and handle cookies robustly.
- Centralized `DEFAULT_LOCALE` logic for better maintainability.
- Refined `useOrderSync` and `useWishlistSync` for clean synchronization across auth states.
- Updated SCSS in hero and header styles for alignment corrections.

Breaking Changes: `DEFAULT_LOCALE` constant removed; replaced with runtime config and fallback logic. Consumers must adapt to `DEFAULT_LOCALE_FALLBACK` and `$appHelpers.DEFAULT_LOCALE`.
This commit is contained in:
Alexandr SaVBaD Waltz 2026-02-28 22:38:45 +03:00
parent a14be696e7
commit 2ea18eb8a6
24 changed files with 391 additions and 194 deletions

View file

@ -1,9 +1,8 @@
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@appConstants'; import { SUPPORTED_LOCALES } from '@appConstants';
export default defineAppConfig({ export default defineAppConfig({
i18n: { i18n: {
supportedLocales: SUPPORTED_LOCALES, supportedLocales: SUPPORTED_LOCALES,
defaultLocale: DEFAULT_LOCALE,
}, },
ui: { ui: {
showBreadcrumbs: true, showBreadcrumbs: true,

View file

@ -38,7 +38,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {DEFAULT_LOCALE} from '@appConstants';
import {useRefresh} from '@composables/auth'; import {useRefresh} from '@composables/auth';
import {useLanguages, useLocaleRedirect} from '@composables/languages'; import {useLanguages, useLocaleRedirect} from '@composables/languages';
import {useCompanyInfo} from '@composables/company'; import {useCompanyInfo} from '@composables/company';
@ -49,11 +48,11 @@ const { locale } = useI18n();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const appStore = useAppStore(); const appStore = useAppStore();
const { $appHelpers } = useNuxtApp();
const { isDemoMode, uiConfig } = useProjectConfig(); const { isDemoMode, uiConfig } = useProjectConfig();
const toaster = { position: uiConfig.value.toastPosition }; const toaster = { position: uiConfig.value.toastPosition };
const switchLocalePath = useSwitchLocalePath(); const switchLocalePath = useSwitchLocalePath();
const { $appHelpers } = useNuxtApp();
const showBreadcrumbs = computed(() => { const showBreadcrumbs = computed(() => {
const name = typeof route.name === 'string' ? route.name : ''; const name = typeof route.name === 'string' ? route.name : '';
@ -77,7 +76,7 @@ const activeState = computed(() => appStore.activeAuthState);
const cookieLocale = useCookie( const cookieLocale = useCookie(
$appHelpers.COOKIES_LOCALE_KEY, $appHelpers.COOKIES_LOCALE_KEY,
{ {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/' path: '/'
} }
); );
@ -116,7 +115,7 @@ watch(locale, () => {
let stopWatcher: VoidFunction = () => {}; let stopWatcher: VoidFunction = () => {};
if (!cookieLocale.value) { if (!cookieLocale.value) {
cookieLocale.value = DEFAULT_LOCALE; cookieLocale.value = $appHelpers.DEFAULT_LOCALE;
await router.push({path: switchLocalePath(cookieLocale.value)}); await router.push({path: switchLocalePath(cookieLocale.value)});
} }
@ -127,10 +126,10 @@ if (locale.value !== cookieLocale.value) {
query: route.query query: route.query
}); });
} else { } else {
cookieLocale.value = DEFAULT_LOCALE cookieLocale.value = $appHelpers.DEFAULT_LOCALE;
await router.push({ await router.push({
path: switchLocalePath(DEFAULT_LOCALE), path: switchLocalePath($appHelpers.DEFAULT_LOCALE),
query: route.query query: route.query
}); });
} }

View file

@ -130,6 +130,11 @@ const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
default: () => [], default: () => [],
path: '/', path: '/',
}); });
const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, {
default: () => [],
path: '/',
});
console.log(cookieCart.value)
const productsInCartQuantity = computed(() => { const productsInCartQuantity = computed(() => {
if (isAuthenticated.value) { if (isAuthenticated.value) {
@ -166,7 +171,7 @@ const redirectTo = (to) => {
<style lang="scss" scoped> <style lang="scss" scoped>
.header { .header {
position: relative; position: relative;
z-index: 3; z-index: 5;
top: 0; top: 0;
left: 0; left: 0;
width: 100vw; width: 100vw;

View file

@ -7,7 +7,8 @@
class="card__wishlist" class="card__wishlist"
@click="overwriteWishlist({ @click="overwriteWishlist({
type: (isProductInWishlist ? 'remove' : 'add'), type: (isProductInWishlist ? 'remove' : 'add'),
product: product, productUuid: product.uuid,
productName: product.name,
})" })"
> >
<icon style="color: #dc2626;" name="mdi:cards-heart" size="16" v-if="isProductInWishlist" /> <icon style="color: #dc2626;" name="mdi:cards-heart" size="16" v-if="isProductInWishlist" />
@ -128,22 +129,56 @@ const props = defineProps<{
const {t} = useI18n(); const {t} = useI18n();
const wishlistStore = useWishlistStore(); const wishlistStore = useWishlistStore();
const cartStore = useCartStore(); const cartStore = useCartStore();
const userStore = useUserStore();
const { $appHelpers } = useNuxtApp();
const { overwriteWishlist } = useWishlistOverwrite(); const { overwriteWishlist } = useWishlistOverwrite();
const { addLoading, removeLoading, overwriteOrder } = useOrderOverwrite(); const { addLoading, removeLoading, overwriteOrder } = useOrderOverwrite();
const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
default: () => [],
path: '/',
});
const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, {
default: () => [],
path: '/',
});
const isAuthenticated = computed(() => userStore.isAuthenticated);
const isProductInWishlist = computed(() => { const isProductInWishlist = computed(() => {
const el = wishlistStore.wishlist?.products?.edges.find( if (isAuthenticated.value) {
return !!wishlistStore.wishlist?.products?.edges.find(
(el) => el?.node?.uuid === props.product.uuid (el) => el?.node?.uuid === props.product.uuid
); );
} else {
return !!el; return (cookieWishlist.value ?? []).includes(props.product.uuid);
}
}); });
const isProductInCart = computed(() => { const isProductInCart = computed(() => {
return cartStore.currentOrder?.orderProducts?.edges.find((prod) => prod.node.product.uuid === props.product?.uuid); if (isAuthenticated.value) {
return !!cartStore.currentOrder?.orderProducts?.edges.find(
(prod) => prod.node.product.uuid === props.product?.uuid
);
} else {
return (cookieCart.value ?? []).some(
(item) => item.product === props.product?.uuid
);
}
}); });
const productInCartQuantity = computed(() => { const productInCartQuantity = computed(() => {
return cartStore.currentOrder?.orderProducts?.edges.filter((prod) => prod.node.product.uuid === props.product.uuid)[0].node.quantity; if (isAuthenticated.value) {
const productEdge = cartStore.currentOrder?.orderProducts?.edges.find(
(prod) => prod.node.product.uuid === props.product.uuid
);
return productEdge?.node.quantity ?? 0;
} else {
const cartItem = (cookieCart.value ?? []).find(
(item) => item.product === props.product.uuid
);
return cartItem?.quantity ?? 0;
}
}); });
const rating = computed(() => { const rating = computed(() => {

View file

@ -48,6 +48,7 @@ const {t} = useI18n();
&__title { &__title {
color: $white; color: $white;
text-align: center;
font-family: "Playfair Display", sans-serif; font-family: "Playfair Display", sans-serif;
font-size: 60px; font-size: 60px;
font-weight: 700; font-weight: 700;

View file

@ -1,4 +1,3 @@
import { DEFAULT_LOCALE } from '@appConstants';
import { useLocaleRedirect } from '@composables/languages'; import { useLocaleRedirect } from '@composables/languages';
import { useNotification } from '@composables/notification'; import { useNotification } from '@composables/notification';
import { useUserBaseData } from '@composables/user'; import { useUserBaseData } from '@composables/user';
@ -22,7 +21,7 @@ export function useLogin() {
path: '/', path: '/',
}); });
const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, { const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/', path: '/',
}); });
@ -43,8 +42,6 @@ export function useLogin() {
userStore.setUser(authData.user); userStore.setUser(authData.user);
cookieAccess.value = authData.accessToken; cookieAccess.value = authData.accessToken;
await nextTick();
navigateTo(localePath('/')); navigateTo(localePath('/'));
useNotification({ useNotification({

View file

@ -1,4 +1,3 @@
import { DEFAULT_LOCALE } from '@appConstants';
import { useLogout } from '@composables/auth'; import { useLogout } from '@composables/auth';
import { useLocaleRedirect } from '@composables/languages'; import { useLocaleRedirect } from '@composables/languages';
import { useNotification } from '@composables/notification'; import { useNotification } from '@composables/notification';
@ -40,7 +39,7 @@ export function useRefresh() {
path: '/', path: '/',
}); });
const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, { const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/', path: '/',
}); });

View file

@ -1,4 +1,4 @@
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@appConstants'; import { SUPPORTED_LOCALES } from '@appConstants';
import { GET_LANGUAGES } from '@graphql/queries/standalone/languages.js'; import { GET_LANGUAGES } from '@graphql/queries/standalone/languages.js';
import type { ILanguage, ILanguagesResponse } from '@types'; import type { ILanguage, ILanguagesResponse } from '@types';
@ -7,7 +7,7 @@ export async function useLanguages() {
const { $appHelpers } = useNuxtApp(); const { $appHelpers } = useNuxtApp();
const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, { const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/', path: '/',
}); });

View file

@ -1,4 +1,3 @@
import { DEFAULT_LOCALE } from '@appConstants';
import { SWITCH_LANGUAGE } from '@graphql/mutations/languages.js'; import { SWITCH_LANGUAGE } from '@graphql/mutations/languages.js';
import type { IUserResponse, LocaleDefinition } from '@types'; import type { IUserResponse, LocaleDefinition } from '@types';
@ -11,7 +10,7 @@ export function useLanguageSwitch() {
const switchLocalePath = useSwitchLocalePath(); const switchLocalePath = useSwitchLocalePath();
const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, { const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/', path: '/',
}); });

View file

@ -1,4 +1,4 @@
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '@appConstants'; import { SUPPORTED_LOCALES } from '@appConstants';
import type { SupportedLocale } from '@types'; import type { SupportedLocale } from '@types';
export function useLocaleRedirect() { export function useLocaleRedirect() {
@ -9,7 +9,7 @@ export function useLocaleRedirect() {
const switchLocalePath = useSwitchLocalePath(); const switchLocalePath = useSwitchLocalePath();
const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, { const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/', path: '/',
}); });
@ -18,7 +18,7 @@ export function useLocaleRedirect() {
} }
async function checkAndRedirect(userLocale: string) { async function checkAndRedirect(userLocale: string) {
const targetLocale = isSupportedLocale(userLocale) ? userLocale : DEFAULT_LOCALE; const targetLocale = isSupportedLocale(userLocale) ? userLocale : $appHelpers.DEFAULT_LOCALE;
if (targetLocale !== locale.value) { if (targetLocale !== locale.value) {
cookieLocale.value = targetLocale; cookieLocale.value = targetLocale;

View file

@ -9,7 +9,6 @@ import {
import type { import type {
IAddToOrderResponse, IAddToOrderResponse,
IBulkOrderResponse, IBulkOrderResponse,
IProduct,
IRemoveAllFromOrderResponse, IRemoveAllFromOrderResponse,
IRemoveFromOrderResponse, IRemoveFromOrderResponse,
IRemoveKindFromOrderResponse, IRemoveKindFromOrderResponse,
@ -17,7 +16,8 @@ import type {
interface IOverwriteOrderArguments { interface IOverwriteOrderArguments {
type: string; type: string;
product: IProduct; productUuid: string;
productName: string;
bulkAction?: string; bulkAction?: string;
isBulkSync?: boolean; isBulkSync?: boolean;
products?: { products?: {
@ -29,6 +29,7 @@ export function useOrderOverwrite() {
const { t } = useI18n(); const { t } = useI18n();
const cartStore = useCartStore(); const cartStore = useCartStore();
const userStore = useUserStore(); const userStore = useUserStore();
const { $appHelpers } = useNuxtApp();
const isAuthenticated = computed(() => userStore.isAuthenticated); const isAuthenticated = computed(() => userStore.isAuthenticated);
const orderUuid = computed(() => cartStore.currentOrder?.uuid); const orderUuid = computed(() => cartStore.currentOrder?.uuid);
@ -36,6 +37,7 @@ export function useOrderOverwrite() {
const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, { const cookieCart = useCookie($appHelpers.COOKIES_CART_KEY, {
default: () => [], default: () => [],
path: '/', path: '/',
watch: true,
}); });
const { const {
@ -70,7 +72,7 @@ export function useOrderOverwrite() {
case 'add': { case 'add': {
const addResult = await addMutate({ const addResult = await addMutate({
orderUuid: orderUuid.value, orderUuid: orderUuid.value,
productUuid: args.product.uuid, productUuid: args.productUuid,
}); });
if (addResult?.data?.addOrderProduct?.order) { if (addResult?.data?.addOrderProduct?.order) {
@ -78,7 +80,7 @@ export function useOrderOverwrite() {
useNotification({ useNotification({
message: t('popup.success.addToCart', { message: t('popup.success.addToCart', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -90,7 +92,7 @@ export function useOrderOverwrite() {
case 'remove': { case 'remove': {
const removeResult = await removeMutate({ const removeResult = await removeMutate({
orderUuid: orderUuid.value, orderUuid: orderUuid.value,
productUuid: args.product.uuid, productUuid: args.productUuid,
}); });
if (removeResult?.data?.removeOrderProduct?.order) { if (removeResult?.data?.removeOrderProduct?.order) {
@ -98,7 +100,7 @@ export function useOrderOverwrite() {
useNotification({ useNotification({
message: t('popup.success.removeFromCart', { message: t('popup.success.removeFromCart', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -110,7 +112,7 @@ export function useOrderOverwrite() {
case 'removeKind': { case 'removeKind': {
const removeKindResult = await removeKindMutate({ const removeKindResult = await removeKindMutate({
orderUuid: orderUuid.value, orderUuid: orderUuid.value,
productUuid: args.product.uuid, productUuid: args.productUuid,
}); });
if (removeKindResult?.data?.removeOrderProductsOfAKind?.order) { if (removeKindResult?.data?.removeOrderProductsOfAKind?.order) {
@ -118,7 +120,7 @@ export function useOrderOverwrite() {
useNotification({ useNotification({
message: t('popup.success.removeFromCart', { message: t('popup.success.removeFromCart', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -168,27 +170,29 @@ export function useOrderOverwrite() {
} else { } else {
switch (args.type) { switch (args.type) {
case 'add': { case 'add': {
const currentCart = cookieCart.value || []; const current = Array.isArray(cookieCart.value) ? [...cookieCart.value] : [];
const existingItem = currentCart.find((item) => item.product.uuid === args.product.uuid);
if (existingItem) { const index = current.findIndex(
existingItem.quantity += 1; (item) => item.product === args.productUuid
cookieCart.value = [ );
...currentCart,
]; if (index !== -1) {
current[index] = {
...current[index],
quantity: current[index].quantity + 1,
};
} else { } else {
cookieCart.value = [ current.push({
...currentCart, product: args.productUuid,
{
product: args.product,
quantity: 1, quantity: 1,
}, });
];
} }
cookieCart.value = current;
useNotification({ useNotification({
message: t('popup.success.addToCart', { message: t('popup.success.addToCart', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -197,22 +201,29 @@ export function useOrderOverwrite() {
} }
case 'remove': { case 'remove': {
const currentCart = cookieCart.value || []; const current = Array.isArray(cookieCart.value)
const existingItem = currentCart.find((item) => item.product.uuid === args.product.uuid); ? [...cookieCart.value]
: [];
if (existingItem) { const index = current.findIndex(
if (existingItem.quantity > 1) { (item) => item.product === args.productUuid
existingItem.quantity -= 1; );
cookieCart.value = [
...currentCart, if (index !== -1) {
]; if (current[index].quantity > 1) {
current[index] = {
...current[index],
quantity: current[index].quantity - 1,
};
} else { } else {
cookieCart.value = currentCart.filter((item) => item.product.uuid !== args.product.uuid); current.splice(index, 1);
} }
cookieCart.value = current;
useNotification({ useNotification({
message: t('popup.success.removeFromCart', { message: t('popup.success.removeFromCart', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -222,11 +233,13 @@ export function useOrderOverwrite() {
} }
case 'removeKind': { case 'removeKind': {
cookieCart.value = cookieCart.value.filter((item) => item.product.uuid !== args.product.uuid); cookieCart.value = (cookieCart.value ?? []).filter(
(item) => item.product.uuid !== args.productUuid
);
useNotification({ useNotification({
message: t('popup.success.removeFromCart', { message: t('popup.success.removeFromCart', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -246,28 +259,18 @@ export function useOrderOverwrite() {
} }
case 'bulk': { case 'bulk': {
if (args.bulkAction === 'remove' && args.products) { const bulkResult = await bulkMutate({
const uuidsToRemove = args.products.map((p) => p.uuid); orderUuid: orderUuid.value,
cookieCart.value = cookieCart.value.filter((item) => !uuidsToRemove.includes(item.product.uuid)); action: args.bulkAction,
products: args.products,
});
if (bulkResult?.data?.bulkOrderAction?.order) {
cartStore.setCurrentOrders(bulkResult.data.bulkOrderAction.order);
useNotification({ useNotification({
message: t('popup.success.bulkRemoveOrder'), message: t('popup.success.bulkRemoveOrder'),
type: 'success', type: 'success',
}); });
} else if (args.bulkAction === 'add' && args.products) {
const currentCart = cookieCart.value || [];
for (const productRef of args.products) {
const existingItem = currentCart.find((item) => item.product.uuid === productRef.uuid);
if (existingItem) {
existingItem.quantity += 1;
}
}
cookieCart.value = [
...currentCart,
];
} }
break; break;
@ -279,21 +282,29 @@ export function useOrderOverwrite() {
} }
} }
watch(addError || removedError || removedKindError || removedAllError || bulkError, (err) => { watch(
[addError, removedError, removedKindError, removedAllError, bulkError],
(errors) => {
const err = errors.find(Boolean);
if (!err) return; if (!err) return;
console.error('useOrderOverwrite error:', err); console.error('useOrderOverwrite error:', err);
let message = t('popup.errors.defaultError'); let message = t('popup.errors.defaultError');
if (isGraphQLError(err)) { if (isGraphQLError(err)) {
message = err.graphQLErrors?.[0]?.message || message; message = err.graphQLErrors?.[0]?.message || message;
} else { } else {
message = err.message; message = err.message;
} }
useNotification({ useNotification({
message, message,
type: 'error', type: 'error',
title: t('popup.errors.main'), title: t('popup.errors.main'),
}); });
}); }
);
return { return {
addLoading, addLoading,

View file

@ -1,4 +1,3 @@
import { DEFAULT_LOCALE } from '@appConstants';
import { useLogout } from '@composables/auth'; import { useLogout } from '@composables/auth';
import { useLocaleRedirect } from '@composables/languages'; import { useLocaleRedirect } from '@composables/languages';
import { useNotification } from '@composables/notification'; import { useNotification } from '@composables/notification';
@ -16,7 +15,7 @@ export function useUserUpdating() {
const { logout } = useLogout(); const { logout } = useLogout();
const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, { const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/', path: '/',
}); });
const userUuid = computed(() => userStore.user?.uuid); const userUuid = computed(() => userStore.user?.uuid);

View file

@ -1,4 +1,4 @@
import { useNotification } from '@composables/notification'; import {useNotification} from '@composables/notification';
import { import {
ADD_TO_WISHLIST, ADD_TO_WISHLIST,
BULK_WISHLIST, BULK_WISHLIST,
@ -8,14 +8,14 @@ import {
import type { import type {
IAddToWishlistResponse, IAddToWishlistResponse,
IBulkWishlistResponse, IBulkWishlistResponse,
IProduct,
IRemoveAllFromWishlistResponse, IRemoveAllFromWishlistResponse,
IRemoveFromWishlistResponse, IRemoveFromWishlistResponse,
} from '@types'; } from '@types';
interface IOverwriteWishlistArguments { interface IOverwriteWishlistArguments {
type: string; type: string;
product: IProduct; productUuid: string;
productName: string;
bulkAction?: string; bulkAction?: string;
isBulkSync?: boolean; isBulkSync?: boolean;
products?: { products?: {
@ -35,6 +35,7 @@ export function useWishlistOverwrite() {
const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, { const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
default: () => [], default: () => [],
path: '/', path: '/',
watch: true,
}); });
const { const {
@ -64,7 +65,7 @@ export function useWishlistOverwrite() {
case 'add': { case 'add': {
const addResult = await addMutate({ const addResult = await addMutate({
wishlistUuid: wishlistUuid.value, wishlistUuid: wishlistUuid.value,
productUuid: args.product.uuid, productUuid: args.productUuid,
}); });
if (addResult?.data?.addWishlistProduct?.wishlist) { if (addResult?.data?.addWishlistProduct?.wishlist) {
@ -72,7 +73,7 @@ export function useWishlistOverwrite() {
useNotification({ useNotification({
message: t('popup.success.addToWishlist', { message: t('popup.success.addToWishlist', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -84,7 +85,7 @@ export function useWishlistOverwrite() {
case 'remove': { case 'remove': {
const removeResult = await removeMutate({ const removeResult = await removeMutate({
wishlistUuid: wishlistUuid.value, wishlistUuid: wishlistUuid.value,
productUuid: args.product.uuid, productUuid: args.productUuid,
}); });
if (removeResult?.data?.removeWishlistProduct?.wishlist) { if (removeResult?.data?.removeWishlistProduct?.wishlist) {
@ -92,7 +93,7 @@ export function useWishlistOverwrite() {
useNotification({ useNotification({
message: t('popup.success.removeFromWishlist', { message: t('popup.success.removeFromWishlist', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -104,7 +105,7 @@ export function useWishlistOverwrite() {
case 'removeAll': { case 'removeAll': {
const removeAllResult = await removeAllMutate({ const removeAllResult = await removeAllMutate({
wishlistUuid: wishlistUuid.value, wishlistUuid: wishlistUuid.value,
productUuid: args.product.uuid, productUuid: args.productUuid,
}); });
if (removeAllResult?.data?.removeAllWishlistProducts?.wishlist) { if (removeAllResult?.data?.removeAllWishlistProducts?.wishlist) {
@ -148,38 +149,43 @@ export function useWishlistOverwrite() {
} else { } else {
switch (args.type) { switch (args.type) {
case 'add': { case 'add': {
const isAlreadyInWishlist = cookieWishlist.value.some((item) => item.uuid === args.product.uuid); const current = Array.isArray(cookieWishlist.value)
? [...cookieWishlist.value]
: [];
if (isAlreadyInWishlist) { if (current.includes(args.productUuid)) {
useNotification({ useNotification({
message: t('popup.errors.alreadyInWishlist', { message: t('popup.errors.alreadyInWishlist', {
product: args.product.name, product: args.productName,
}), }),
type: 'warning', type: 'warning',
}); });
} else { return;
cookieWishlist.value = [ }
...cookieWishlist.value,
args.product, current.push(args.productUuid);
]; cookieWishlist.value = current;
useNotification({ useNotification({
message: t('popup.success.addToWishlist', { message: t('popup.success.addToWishlist', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
}
break; break;
} }
case 'remove': { case 'remove': {
cookieWishlist.value = cookieWishlist.value.filter((item) => item.uuid !== args.product.uuid); cookieWishlist.value = Array.isArray(cookieWishlist.value)
? cookieWishlist.value.filter(
(uuid) => uuid !== args.productUuid
)
: [];
useNotification({ useNotification({
message: t('popup.success.removeFromWishlist', { message: t('popup.success.removeFromWishlist', {
product: args.product.name, product: args.productName,
}), }),
type: 'success', type: 'success',
}); });
@ -227,7 +233,10 @@ export function useWishlistOverwrite() {
} }
} }
watch(addError || removedError || removeAllError || bulkError, (err) => { watch(
[addError, removedError, removeAllError, bulkError],
(errors) => {
const err = errors.find(Boolean);
if (!err) return; if (!err) return;
console.error('useWishlistOverwrite error:', err); console.error('useWishlistOverwrite error:', err);
let message = t('popup.errors.defaultError'); let message = t('popup.errors.defaultError');
@ -241,7 +250,8 @@ export function useWishlistOverwrite() {
type: 'error', type: 'error',
title: t('popup.errors.main'), title: t('popup.errors.main'),
}); });
}); }
);
return { return {
addLoading, addLoading,

View file

@ -28,7 +28,7 @@ export function useWishlistSync() {
const apiProductUuids = wishlistStore.wishlist?.products?.edges.map((e) => e.node.uuid) || []; const apiProductUuids = wishlistStore.wishlist?.products?.edges.map((e) => e.node.uuid) || [];
const productsToAdd = cookieProducts.filter((product) => !apiProductUuids.includes(product.uuid)); const productsToAdd = cookieProducts.filter((product) => !apiProductUuids.includes(product));
if (productsToAdd.length === 0) { if (productsToAdd.length === 0) {
cookieWishlist.value = []; cookieWishlist.value = [];
@ -41,7 +41,7 @@ export function useWishlistSync() {
bulkAction: 'add', bulkAction: 'add',
isBulkSync: true, isBulkSync: true,
products: productsToAdd.map((p) => ({ products: productsToAdd.map((p) => ({
uuid: p.uuid, uuid: p,
})), })),
}); });

View file

@ -3,8 +3,7 @@ import type { LocaleDefinition } from '@types';
export const SUPPORTED_LOCALES: LocaleDefinition[] = [ export const SUPPORTED_LOCALES: LocaleDefinition[] = [
{ {
code: 'en-gb', code: 'en-gb',
file: 'en-gb.json', file: 'en-gb.json'
default: true,
}, },
// { // {
// code: 'ar-ar', // code: 'ar-ar',
@ -73,8 +72,7 @@ export const SUPPORTED_LOCALES: LocaleDefinition[] = [
// }, // },
{ {
code: 'ru-ru', code: 'ru-ru',
file: 'ru-ru.json', file: 'ru-ru.json'
default: false,
}, },
// { // {
// code: 'zh-hans', // code: 'zh-hans',
@ -83,4 +81,4 @@ export const SUPPORTED_LOCALES: LocaleDefinition[] = [
// }, // },
]; ];
export const DEFAULT_LOCALE = SUPPORTED_LOCALES.find((locale) => locale.default)?.code || 'en-gb'; export const DEFAULT_LOCALE_FALLBACK = 'en-gb';

View file

@ -79,7 +79,8 @@
class="product__main-button" class="product__main-button"
@click="overwriteWishlist({ @click="overwriteWishlist({
type: (isProductInWishlist ? 'remove' : 'add'), type: (isProductInWishlist ? 'remove' : 'add'),
product: product productUuid: product.uuid,
productName: product.name,
})" })"
:type="'button'" :type="'button'"
:style="'secondary'" :style="'secondary'"
@ -165,6 +166,7 @@ const route = useRoute();
const {t, locale} = useI18n(); const {t, locale} = useI18n();
const wishlistStore = useWishlistStore(); const wishlistStore = useWishlistStore();
const cartStore = useCartStore(); const cartStore = useCartStore();
const userStore = useUserStore();
const { $appHelpers } = useNuxtApp(); const { $appHelpers } = useNuxtApp();
const { setPageTitle } = usePageTitle(); const { setPageTitle } = usePageTitle();
@ -176,6 +178,13 @@ const { overwriteWishlist } = useWishlistOverwrite();
const { addLoading, removeLoading, overwriteOrder } = useOrderOverwrite(); const { addLoading, removeLoading, overwriteOrder } = useOrderOverwrite();
const { product, seoMeta } = await useProductBySlug(slug.value); const { product, seoMeta } = await useProductBySlug(slug.value);
const cookieWishlist = useCookie($appHelpers.COOKIES_WISHLIST_KEY, {
default: () => [],
path: '/',
});
const isAuthenticated = computed(() => userStore.isAuthenticated);
const meta = useDefaultSeo(seoMeta.value || null); const meta = useDefaultSeo(seoMeta.value || null);
if (meta) { if (meta) {
@ -211,18 +220,39 @@ if (meta) {
const { products } = useProducts({ categoriesSlugs: product.value?.category.slug }); const { products } = useProducts({ categoriesSlugs: product.value?.category.slug });
const isProductInWishlist = computed(() => { const isProductInWishlist = computed(() => {
const el = wishlistStore.wishlist?.products?.edges.find( if (isAuthenticated.value) {
(el) => el?.node?.uuid === product.value?.uuid return !!wishlistStore.wishlist?.products?.edges.find(
(el) => el?.node?.uuid === product.value.uuid
); );
} else {
return !!el; return (cookieWishlist.value ?? []).includes(product.value.uuid);
}
}); });
const isProductInCart = computed(() => { const isProductInCart = computed(() => {
return cartStore.currentOrder?.orderProducts?.edges.find((prod) => prod.node.product.uuid === product.value?.uuid); if (isAuthenticated.value) {
return !!cartStore.currentOrder?.orderProducts?.edges.find(
(prod) => prod.node.product.uuid === product.value?.uuid
);
} else {
return (cookieCart.value ?? []).some(
(item) => item.product.uuid === product.value?.uuid
);
}
}); });
const productInCartQuantity = computed(() => { const productInCartQuantity = computed(() => {
return cartStore.currentOrder?.orderProducts?.edges.filter((prod) => prod.node.product.uuid === product.value.uuid)[0].node.quantity; if (isAuthenticated.value) {
const productEdge = cartStore.currentOrder?.orderProducts?.edges.find(
(prod) => prod.node.product.uuid === product.value.uuid
);
return productEdge?.node.quantity ?? 0;
} else {
const cartItem = (cookieCart.value ?? []).find(
(item) => item.product === product.value.uuid
);
return cartItem?.quantity ?? 0;
}
}); });
const images = computed<string[]>(() => const images = computed<string[]>(() =>

View file

@ -49,7 +49,6 @@
<script setup lang="ts"> <script setup lang="ts">
import {usePageTitle} from '@composables/utils'; import {usePageTitle} from '@composables/utils';
import { useDate } from '@composables/date'; import { useDate } from '@composables/date';
import {DEFAULT_LOCALE} from '@appConstants';
import {useAvatarUpload} from '@composables/user'; import {useAvatarUpload} from '@composables/user';
import {useNotification} from '@composables/notification'; import {useNotification} from '@composables/notification';
@ -62,7 +61,7 @@ const { uploadAvatar } = useAvatarUpload();
const cookieLocale = useCookie( const cookieLocale = useCookie(
$appHelpers. COOKIES_LOCALE_KEY, $appHelpers. COOKIES_LOCALE_KEY,
{ {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/' path: '/'
} }
); );
@ -77,9 +76,9 @@ const joinData = computed(() => {
const referralLink = computed(() => { const referralLink = computed(() => {
if (finishedOrdersQuantity.value > 0) { if (finishedOrdersQuantity.value > 0) {
return `https://${$appHelpers.APP_DOMAIN}/${DEFAULT_LOCALE}/?referrer=` + user.value?.uuid; return `https://${$appHelpers.APP_DOMAIN}/${$appHelpers.DEFAULT_LOCALE}/?referrer=` + user.value?.uuid;
} else { } else {
return `https://${$appHelpers.APP_DOMAIN}/${DEFAULT_LOCALE}/`; return `https://${$appHelpers.APP_DOMAIN}/${$appHelpers.DEFAULT_LOCALE}/`;
} }
}); });

View file

@ -22,7 +22,6 @@
import {usePageTitle} from '@composables/utils'; import {usePageTitle} from '@composables/utils';
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const { $appHelpers } = useNuxtApp();
const { setPageTitle } = usePageTitle(); const { setPageTitle } = usePageTitle();

View file

@ -1,9 +1,14 @@
import { COOKIE_KEY_TEMPLATES } from '~/constants'; import { COOKIE_KEY_TEMPLATES, DEFAULT_LOCALE_FALLBACK, SUPPORTED_LOCALES } from '~/constants';
import { createProjectKey } from "~/utils/transliterator";
export default defineNuxtPlugin(() => { export default defineNuxtPlugin(() => {
const runtimeConfig = useRuntimeConfig(); const runtimeConfig = useRuntimeConfig();
const APP_NAME = runtimeConfig.public.schonProjectName as string; const APP_NAME = runtimeConfig.public.schonProjectName as string;
const APP_NAME_KEY = APP_NAME.toLowerCase().replace(/\s+/g, '-'); const APP_NAME_KEY = createProjectKey(APP_NAME);
const envLocale = runtimeConfig.public.schonLanguageCode as string;
const isValid = SUPPORTED_LOCALES.some(locale => locale.code === envLocale);
const DEFAULT_LOCALE = isValid ? envLocale : DEFAULT_LOCALE_FALLBACK;
return { return {
provide: { provide: {
@ -11,6 +16,7 @@ export default defineNuxtPlugin(() => {
APP_DOMAIN: runtimeConfig.public.schonBaseDomain, APP_DOMAIN: runtimeConfig.public.schonBaseDomain,
APP_NAME, APP_NAME,
APP_NAME_KEY, APP_NAME_KEY,
DEFAULT_LOCALE,
COOKIES_LOCALE_KEY: COOKIE_KEY_TEMPLATES.LOCALE(APP_NAME_KEY), COOKIES_LOCALE_KEY: COOKIE_KEY_TEMPLATES.LOCALE(APP_NAME_KEY),
COOKIES_REFRESH_TOKEN_KEY: COOKIE_KEY_TEMPLATES.REFRESH_TOKEN(APP_NAME_KEY), COOKIES_REFRESH_TOKEN_KEY: COOKIE_KEY_TEMPLATES.REFRESH_TOKEN(APP_NAME_KEY),
COOKIES_ACCESS_TOKEN_KEY: COOKIE_KEY_TEMPLATES.ACCESS_TOKEN(APP_NAME_KEY), COOKIES_ACCESS_TOKEN_KEY: COOKIE_KEY_TEMPLATES.ACCESS_TOKEN(APP_NAME_KEY),

View file

@ -1,11 +1,10 @@
import { DEFAULT_LOCALE } from '@appConstants';
import type { ILanguage } from '@types'; import type { ILanguage } from '@types';
export const useLanguageStore = defineStore('language', () => { export const useLanguageStore = defineStore('language', () => {
const { $appHelpers } = useNuxtApp(); const { $appHelpers } = useNuxtApp();
const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, { const cookieLocale = useCookie($appHelpers.COOKIES_LOCALE_KEY, {
default: () => DEFAULT_LOCALE, default: () => $appHelpers.DEFAULT_LOCALE,
path: '/', path: '/',
}); });

View file

@ -0,0 +1,99 @@
export function transliterate(str: string | undefined): string {
if (!str) return '';
const map = {
// Russian/Ukrainian/Belarusian Cyrillic
'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D', 'Е': 'E', 'Ё': 'Yo', 'Ж': 'Zh',
'З': 'Z', 'И': 'I', 'Й': 'Y', 'К': 'K', 'Л': 'L', 'М': 'M', 'Н': 'N', 'О': 'O',
'П': 'P', 'Р': 'R', 'С': 'S', 'Т': 'T', 'У': 'U', 'Ф': 'F', 'Х': 'Kh', 'Ц': 'Ts',
'Ч': 'Ch', 'Ш': 'Sh', 'Щ': 'Shch', 'Ъ': '', 'Ы': 'Y', 'Ь': '', 'Э': 'E', 'Ю': 'Yu',
'Я': 'Ya',
'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo', 'ж': 'zh',
'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o',
'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'kh', 'ц': 'ts',
'ч': 'ch', 'ш': 'sh', 'щ': 'shch', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu',
'я': 'ya',
// Ukrainian extras
'Ґ': 'G', 'ґ': 'g', 'Є': 'Ye', 'є': 'ye', 'І': 'I', 'і': 'i', 'Ї': 'Yi', 'ї': 'yi',
// Serbian/Macedonian Cyrillic extras
'Ђ': 'Dj', 'ђ': 'dj', 'Ј': 'J', 'ј': 'j', 'Љ': 'Lj', 'љ': 'lj', 'Њ': 'Nj', 'њ': 'nj',
'Ћ': 'C', 'ћ': 'c', 'Џ': 'Dz', 'џ': 'dz', 'Ѓ': 'Gj', 'ѓ': 'gj', 'Ѕ': 'Dz', 'ѕ': 'dz',
'Ќ': 'Kj', 'ќ': 'kj',
// Belarusian extras
'Ў': 'U', 'ў': 'u',
// Bulgarian extras (mostly covered above, but ъ is different)
// In Bulgarian context ъ = 'a', but we keep '' as default for Russian
// Greek
'Α': 'A', 'Β': 'V', 'Γ': 'G', 'Δ': 'D', 'Ε': 'E', 'Ζ': 'Z', 'Η': 'I', 'Θ': 'Th',
'Ι': 'I', 'Κ': 'K', 'Λ': 'L', 'Μ': 'M', 'Ν': 'N', 'Ξ': 'X', 'Ο': 'O', 'Π': 'P',
'Ρ': 'R', 'Σ': 'S', 'Τ': 'T', 'Υ': 'Y', 'Φ': 'F', 'Χ': 'Ch', 'Ψ': 'Ps', 'Ω': 'O',
'α': 'a', 'β': 'v', 'γ': 'g', 'δ': 'd', 'ε': 'e', 'ζ': 'z', 'η': 'i', 'θ': 'th',
'ι': 'i', 'κ': 'k', 'λ': 'l', 'μ': 'm', 'ν': 'n', 'ξ': 'x', 'ο': 'o', 'π': 'p',
'ρ': 'r', 'σ': 's', 'ς': 's', 'τ': 't', 'υ': 'y', 'φ': 'f', 'χ': 'ch', 'ψ': 'ps', 'ω': 'o',
// Arabic
'ا': 'a', 'ب': 'b', 'ت': 't', 'ث': 'th', 'ج': 'j', 'ح': 'h', 'خ': 'kh', 'د': 'd',
'ذ': 'dh', 'ر': 'r', 'ز': 'z', 'س': 's', 'ش': 'sh', 'ص': 's', 'ض': 'd', 'ط': 't',
'ظ': 'z', 'ع': 'a', 'غ': 'gh', 'ف': 'f', 'ق': 'q', 'ك': 'k', 'ل': 'l', 'م': 'm',
'ن': 'n', 'ه': 'h', 'و': 'w', 'ي': 'y', 'ة': 'h', 'ء': '\'', 'آ': 'aa',
// German/Nordic/Central European diacritics
'Ä': 'Ae', 'ä': 'ae', 'Ö': 'Oe', 'ö': 'oe', 'Ü': 'Ue', 'ü': 'ue', 'ß': 'ss',
'Å': 'A', 'å': 'a', 'Æ': 'Ae', 'æ': 'ae', 'Ø': 'O', 'ø': 'o',
'Ð': 'D', 'ð': 'd', 'Þ': 'Th', 'þ': 'th',
// Czech/Slovak/Polish/Hungarian/Romanian/Turkish
'Č': 'C', 'č': 'c', 'Ď': 'D', 'ď': 'd', 'Ě': 'E', 'ě': 'e', 'Ň': 'N', 'ň': 'n',
'Ř': 'R', 'ř': 'r', 'Š': 'S', 'š': 's', 'Ť': 'T', 'ť': 't', 'Ů': 'U', 'ů': 'u',
'Ž': 'Z', 'ž': 'z', 'Ľ': 'L', 'ľ': 'l', 'Ĺ': 'L', 'ĺ': 'l', 'Ŕ': 'R', 'ŕ': 'r',
'Ą': 'A', 'ą': 'a', 'Ć': 'C', 'ć': 'c', 'Ę': 'E', 'ę': 'e', 'Ł': 'L', 'ł': 'l',
'Ń': 'N', 'ń': 'n', 'Ś': 'S', 'ś': 's', 'Ź': 'Z', 'ź': 'z', 'Ż': 'Z', 'ż': 'z',
'Á': 'A', 'á': 'a', 'É': 'E', 'é': 'e', 'Í': 'I', 'í': 'i', 'Ó': 'O', 'ó': 'o',
'Ú': 'U', 'ú': 'u', 'Ý': 'Y', 'ý': 'y',
'Ő': 'O', 'ő': 'o', 'Ű': 'U', 'ű': 'u',
'Ă': 'A', 'ă': 'a', 'Â': 'A', 'â': 'a', 'Î': 'I', 'î': 'i', 'Ș': 'S', 'ș': 's', 'Ț': 'T', 'ț': 't',
'Ç': 'C', 'ç': 'c', 'Ğ': 'G', 'ğ': 'g', 'İ': 'I', 'ı': 'i', 'Ş': 'S', 'ş': 's',
// Vietnamese (basic - stripped of tone marks conceptually)
'Đ': 'D', 'đ': 'd',
// Spanish/Portuguese/French
'Ñ': 'N', 'ñ': 'n',
'À': 'A', 'à': 'a', 'È': 'E', 'è': 'e', 'Ì': 'I', 'ì': 'i', 'Ò': 'O', 'ò': 'o',
'Ù': 'U', 'ù': 'u', 'Ë': 'E', 'ë': 'e', 'Ï': 'I', 'ï': 'i',
'Ê': 'E', 'ê': 'e', 'Ô': 'O', 'ô': 'o', 'Û': 'U', 'û': 'u',
'Ã': 'A', 'ã': 'a', 'Õ': 'O', 'õ': 'o',
};
return str.split('').map(ch => {
if (ch in map) return map[ch];
const normalized = ch
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
if (normalized !== ch && /^[\x20-\x7E]+$/.test(normalized)) {
return normalized;
}
return ch;
}).join('');
}
export function createProjectKey(str: string | undefined): string {
if (!str) return 'default-project';
return transliterate(str)
.toLowerCase()
.replace(/['"]/g, '')
.replace(/[^a-z0-9\s-]/g, '')
.trim()
.replace(/\s+/g, '-')
.replace(/-+/g, '-');
}

View file

@ -1,8 +1,16 @@
import type { NuxtI18nOptions } from '@nuxtjs/i18n'; import type { NuxtI18nOptions } from '@nuxtjs/i18n';
import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from '../app/constants'; import { SUPPORTED_LOCALES } from '../app/constants';
import { createProjectKey } from "../app/utils/transliterator";
export const i18nConfig: NuxtI18nOptions = { export function createI18nConfig(defaultLocaleCode?: string, projectName: string): NuxtI18nOptions {
defaultLocale: DEFAULT_LOCALE, const defaultLocale = defaultLocaleCode && SUPPORTED_LOCALES.some(l => l.code === defaultLocaleCode)
? defaultLocaleCode
: 'en-gb';
const cookieKey = `${createProjectKey(projectName)}-locale`;
return {
defaultLocale,
locales: SUPPORTED_LOCALES.map((locale) => ({ locales: SUPPORTED_LOCALES.map((locale) => ({
code: locale.code, code: locale.code,
file: locale.file, file: locale.file,
@ -12,7 +20,8 @@ export const i18nConfig: NuxtI18nOptions = {
detectBrowserLanguage: { detectBrowserLanguage: {
alwaysRedirect: true, alwaysRedirect: true,
redirectOn: 'root', redirectOn: 'root',
fallbackLocale: DEFAULT_LOCALE, fallbackLocale: defaultLocale,
cookieKey: 'schon-locale', cookieKey,
}, },
}; };
}

View file

@ -7,6 +7,7 @@ declare module '#app' {
APP_DOMAIN: string; APP_DOMAIN: string;
APP_NAME: string; APP_NAME: string;
APP_NAME_KEY: string; APP_NAME_KEY: string;
DEFAULT_LOCALE: string;
COOKIES_LOCALE_KEY: string; COOKIES_LOCALE_KEY: string;
COOKIES_REFRESH_TOKEN_KEY: string; COOKIES_REFRESH_TOKEN_KEY: string;
COOKIES_ACCESS_TOKEN_KEY: string; COOKIES_ACCESS_TOKEN_KEY: string;

View file

@ -1,6 +1,7 @@
import { resolve } from 'node:path'; import { resolve } from 'node:path';
import { fileURLToPath, URL } from 'node:url'; import { fileURLToPath, URL } from 'node:url';
import { i18nConfig } from './i18n/i18.config'; import { createProjectKey } from './app/utils/transliterator';
import { createI18nConfig } from './i18n/i18.config';
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: '2025-07-15', compatibilityDate: '2025-07-15',
@ -22,7 +23,7 @@ export default defineNuxtConfig({
'@nuxt/hints', '@nuxt/hints',
'@nuxt/image', '@nuxt/image',
], ],
i18n: i18nConfig, i18n: createI18nConfig(process.env.SCHON_LANGUAGE_CODE),
apollo: { apollo: {
autoImports: true, autoImports: true,
clients: { clients: {
@ -32,7 +33,7 @@ export default defineNuxtConfig({
authType: 'Bearer', authType: 'Bearer',
authHeader: 'X-SCHON-AUTH', authHeader: 'X-SCHON-AUTH',
tokenStorage: 'cookie', tokenStorage: 'cookie',
tokenName: `${process.env.SCHON_PROJECT_NAME?.toLowerCase().replace(/\s+/g, '-')}-access`, tokenName: `${createProjectKey(process.env.SCHON_PROJECT_NAME)}-access`,
}, },
}, },
}, },
@ -40,6 +41,7 @@ export default defineNuxtConfig({
public: { public: {
schonProjectName: process.env.SCHON_PROJECT_NAME, schonProjectName: process.env.SCHON_PROJECT_NAME,
schonBaseDomain: process.env.SCHON_BASE_DOMAIN, schonBaseDomain: process.env.SCHON_BASE_DOMAIN,
schonLanguageCode: process.env.SCHON_LANGUAGE_CODE,
}, },
}, },
app: { app: {
@ -75,6 +77,7 @@ export default defineNuxtConfig({
'@appConstants': fileURLToPath(new URL('./app/constants', import.meta.url)), '@appConstants': fileURLToPath(new URL('./app/constants', import.meta.url)),
'@composables': fileURLToPath(new URL('./app/composables', import.meta.url)), '@composables': fileURLToPath(new URL('./app/composables', import.meta.url)),
'@types': fileURLToPath(new URL('./types', import.meta.url)), '@types': fileURLToPath(new URL('./types', import.meta.url)),
'@utils': fileURLToPath(new URL('./app/utils', import.meta.url)),
}, },
vite: { vite: {
envDir: '../', envDir: '../',