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`.
243 lines
No EOL
5.2 KiB
Vue
243 lines
No EOL
5.2 KiB
Vue
<template>
|
|
<div class="settings">
|
|
<div class="settings__top">
|
|
<div class="settings__top-left">
|
|
<div class="settings__avatar">
|
|
<input type="file" id="avatar" @change="uploadAvatar" />
|
|
<label for="avatar">
|
|
<nuxt-img
|
|
class="settings__avatar-image"
|
|
v-if="user?.avatar"
|
|
:src="user?.avatar"
|
|
alt="avatar"
|
|
format="webp"
|
|
densities="x1"
|
|
/>
|
|
<icon name="clarity:avatar-line" size="40" v-else />
|
|
<span class="settings__avatar-inner">
|
|
<icon name="material-symbols:upload" size="40" />
|
|
</span>
|
|
</label>
|
|
</div>
|
|
<div class="settings__top-inner">
|
|
<h2>{{ user?.firstName }} {{ user?.lastName }}</h2>
|
|
<p>{{ t('profile.settings.joinData') }}: {{ joinData }}</p>
|
|
</div>
|
|
</div>
|
|
<el-tooltip
|
|
:content="t('profile.settings.referralTooltip')"
|
|
placement="top-end"
|
|
:disabled="finishedOrdersQuantity > 0"
|
|
>
|
|
<button
|
|
class="settings__button"
|
|
@click="copyReferral"
|
|
:disabled="finishedOrdersQuantity === 0"
|
|
>
|
|
<icon name="material-symbols:content-copy" size="20" />
|
|
{{ t('profile.settings.copyReferral') }}
|
|
</button>
|
|
</el-tooltip>
|
|
</div>
|
|
<div class="settings__main">
|
|
<p>{{ t('profile.settings.accountInfo') }}</p>
|
|
<forms-update />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {usePageTitle} from '@composables/utils';
|
|
import { useDate } from '@composables/date';
|
|
import {useAvatarUpload} from '@composables/user';
|
|
import {useNotification} from '@composables/notification';
|
|
|
|
const {t} = useI18n();
|
|
const userStore = useUserStore();
|
|
const { $appHelpers } = useNuxtApp();
|
|
|
|
const { uploadAvatar } = useAvatarUpload();
|
|
|
|
const cookieLocale = useCookie(
|
|
$appHelpers. COOKIES_LOCALE_KEY,
|
|
{
|
|
default: () => $appHelpers.DEFAULT_LOCALE,
|
|
path: '/'
|
|
}
|
|
);
|
|
|
|
const user = computed(() => userStore.user);
|
|
const finishedOrdersQuantity = computed(() => userStore.finishedOrdersQuantity);
|
|
const joinData = computed(() => {
|
|
return useDate(
|
|
user.value?.dateJoined, cookieLocale.value
|
|
);
|
|
});
|
|
|
|
const referralLink = computed(() => {
|
|
if (finishedOrdersQuantity.value > 0) {
|
|
return `https://${$appHelpers.APP_DOMAIN}/${$appHelpers.DEFAULT_LOCALE}/?referrer=` + user.value?.uuid;
|
|
} else {
|
|
return `https://${$appHelpers.APP_DOMAIN}/${$appHelpers.DEFAULT_LOCALE}/`;
|
|
}
|
|
});
|
|
|
|
const copyReferral = () => {
|
|
if (finishedOrdersQuantity.value > 0) {
|
|
navigator.clipboard.writeText(referralLink.value)
|
|
.then(() => {
|
|
useNotification({
|
|
message: t('popup.success.referralCopy'),
|
|
type: 'success'
|
|
});
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
});
|
|
}
|
|
};
|
|
|
|
const { setPageTitle } = usePageTitle();
|
|
|
|
setPageTitle(t('breadcrumbs.settings'));
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.settings {
|
|
background-color: $white;
|
|
width: 100%;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
height: fit-content;
|
|
|
|
&__top {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 25px;
|
|
padding: 24px 32px;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
|
|
&-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 25px;
|
|
}
|
|
|
|
&-inner {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 5px;
|
|
|
|
& h2 {
|
|
color: #111827;
|
|
font-family: "Playfair Display", sans-serif;
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
& p {
|
|
color: #4b5563;
|
|
font-size: 16px;
|
|
font-weight: 400;
|
|
}
|
|
}
|
|
}
|
|
|
|
&__avatar {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 50%;
|
|
border: 2px solid #e5e7eb;
|
|
background-color: rgba($accent, 0.2);
|
|
position: relative;
|
|
overflow: hidden;
|
|
|
|
@include hover {
|
|
.settings__avatar-inner {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
& label {
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
|
|
& span {
|
|
color: #a9a9a9;
|
|
}
|
|
}
|
|
|
|
&-image {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
&-inner {
|
|
position: absolute;
|
|
z-index: 1;
|
|
width: 100%;
|
|
height: 100%;
|
|
inset: 0;
|
|
display: grid;
|
|
place-items: center;
|
|
opacity: 0;
|
|
transition: 0.2s;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
& span {
|
|
color: $white !important;
|
|
}
|
|
}
|
|
|
|
& input {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
&__button {
|
|
cursor: pointer;
|
|
border-radius: $default_border_radius;
|
|
padding: 5px 15px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
transition: 0.2s;
|
|
|
|
color: #4b5563;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
|
|
@include hover {
|
|
background-color: #c0c0c0;
|
|
color: #373f49;
|
|
}
|
|
|
|
&:disabled {
|
|
cursor: not-allowed;
|
|
|
|
@include hover {
|
|
background-color: #c0c0c0;
|
|
color: #4b5563;
|
|
}
|
|
}
|
|
}
|
|
|
|
&__main {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 25px;
|
|
padding: 24px 32px;
|
|
|
|
& p {
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
color: $accentDark;
|
|
}
|
|
}
|
|
}
|
|
</style> |