Add role switcher (Client/Seller) in navigation menu
All checks were successful
Build Docker Image / build (push) Successful in 4m20s
All checks were successful
Build Docker Image / build (push) Successful in 4m20s
- Add role switcher buttons after Explore/Quote/Кабинет with separator - Dynamic center tabs based on role: BUYER shows orders/addresses, SELLER shows offers - Redirect to appropriate page when switching role in client area - Add localization for roles.client and roles.seller
This commit is contained in:
@@ -42,6 +42,30 @@
|
|||||||
>
|
>
|
||||||
{{ $t('cabinetNav.cabinet') }}
|
{{ $t('cabinetNav.cabinet') }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|
||||||
|
<!-- Role switcher -->
|
||||||
|
<div v-if="loggedIn && (hasBuyerTeam || hasSellerTeam)" class="flex items-center gap-1 ml-2 pl-2 border-l border-white/20">
|
||||||
|
<button
|
||||||
|
v-if="currentRole === 'BUYER' || hasBuyerTeam"
|
||||||
|
class="px-3 py-1 text-sm font-medium rounded-lg transition-colors"
|
||||||
|
:class="currentRole === 'BUYER'
|
||||||
|
? (useWhiteText ? 'bg-white/20 text-white' : 'bg-base-300 text-base-content')
|
||||||
|
: (useWhiteText ? 'text-white/70 hover:text-white hover:bg-white/10' : 'text-base-content/70 hover:text-base-content hover:bg-base-200')"
|
||||||
|
@click="$emit('switch-role', 'BUYER')"
|
||||||
|
>
|
||||||
|
{{ $t('cabinetNav.roles.client') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="currentRole === 'SELLER' || hasSellerTeam"
|
||||||
|
class="px-3 py-1 text-sm font-medium rounded-lg transition-colors"
|
||||||
|
:class="currentRole === 'SELLER'
|
||||||
|
? (useWhiteText ? 'bg-white/20 text-white' : 'bg-base-300 text-base-content')
|
||||||
|
: (useWhiteText ? 'text-white/70 hover:text-white hover:bg-white/10' : 'text-base-content/70 hover:text-base-content hover:bg-base-200')"
|
||||||
|
@click="$emit('switch-role', 'SELLER')"
|
||||||
|
>
|
||||||
|
{{ $t('cabinetNav.roles.seller') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -53,28 +77,34 @@
|
|||||||
<!-- Client Area tabs -->
|
<!-- Client Area tabs -->
|
||||||
<template v-if="isClientArea">
|
<template v-if="isClientArea">
|
||||||
<div class="flex items-center gap-1 rounded-full border border-white/20 bg-white/80 backdrop-blur-md shadow-lg p-1">
|
<div class="flex items-center gap-1 rounded-full border border-white/20 bg-white/80 backdrop-blur-md shadow-lg p-1">
|
||||||
<NuxtLink
|
<!-- BUYER tabs -->
|
||||||
:to="localePath('/clientarea/orders')"
|
<template v-if="currentRole !== 'SELLER'">
|
||||||
class="px-4 py-2 rounded-full text-sm font-medium transition-colors whitespace-nowrap"
|
<NuxtLink
|
||||||
:class="isClientAreaTabActive('/clientarea/orders') ? 'bg-primary text-primary-content' : 'text-base-content/70 hover:text-base-content hover:bg-base-200/50'"
|
:to="localePath('/clientarea/orders')"
|
||||||
>
|
class="px-4 py-2 rounded-full text-sm font-medium transition-colors whitespace-nowrap"
|
||||||
{{ $t('cabinetNav.orders') }}
|
:class="isClientAreaTabActive('/clientarea/orders') ? 'bg-primary text-primary-content' : 'text-base-content/70 hover:text-base-content hover:bg-base-200/50'"
|
||||||
</NuxtLink>
|
>
|
||||||
<NuxtLink
|
{{ $t('cabinetNav.orders') }}
|
||||||
v-if="isSeller"
|
</NuxtLink>
|
||||||
:to="localePath('/clientarea/offers')"
|
<NuxtLink
|
||||||
class="px-4 py-2 rounded-full text-sm font-medium transition-colors whitespace-nowrap"
|
:to="localePath('/clientarea/addresses')"
|
||||||
:class="isClientAreaTabActive('/clientarea/offers') ? 'bg-primary text-primary-content' : 'text-base-content/70 hover:text-base-content hover:bg-base-200/50'"
|
class="px-4 py-2 rounded-full text-sm font-medium transition-colors whitespace-nowrap"
|
||||||
>
|
:class="isClientAreaTabActive('/clientarea/addresses') ? 'bg-primary text-primary-content' : 'text-base-content/70 hover:text-base-content hover:bg-base-200/50'"
|
||||||
{{ $t('cabinetNav.myOffers') }}
|
>
|
||||||
</NuxtLink>
|
{{ $t('cabinetNav.addresses') }}
|
||||||
<NuxtLink
|
</NuxtLink>
|
||||||
:to="localePath('/clientarea/addresses')"
|
</template>
|
||||||
class="px-4 py-2 rounded-full text-sm font-medium transition-colors whitespace-nowrap"
|
|
||||||
:class="isClientAreaTabActive('/clientarea/addresses') ? 'bg-primary text-primary-content' : 'text-base-content/70 hover:text-base-content hover:bg-base-200/50'"
|
<!-- SELLER tabs -->
|
||||||
>
|
<template v-else>
|
||||||
{{ $t('cabinetNav.addresses') }}
|
<NuxtLink
|
||||||
</NuxtLink>
|
:to="localePath('/clientarea/offers')"
|
||||||
|
class="px-4 py-2 rounded-full text-sm font-medium transition-colors whitespace-nowrap"
|
||||||
|
:class="isClientAreaTabActive('/clientarea/offers') ? 'bg-primary text-primary-content' : 'text-base-content/70 hover:text-base-content hover:bg-base-200/50'"
|
||||||
|
>
|
||||||
|
{{ $t('cabinetNav.myOffers') }}
|
||||||
|
</NuxtLink>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -330,6 +360,10 @@ const props = withDefaults(defineProps<{
|
|||||||
teams?: Array<{ id?: string; name?: string; logtoOrgId?: string }>
|
teams?: Array<{ id?: string; name?: string; logtoOrgId?: string }>
|
||||||
} | null
|
} | null
|
||||||
isSeller?: boolean
|
isSeller?: boolean
|
||||||
|
// Role switching props
|
||||||
|
hasBuyerTeam?: boolean
|
||||||
|
hasSellerTeam?: boolean
|
||||||
|
currentRole?: string
|
||||||
// Search props
|
// Search props
|
||||||
activeTokens?: Array<{ type: string; id: string; label: string; icon: string }>
|
activeTokens?: Array<{ type: string; id: string; label: string; icon: string }>
|
||||||
availableChips?: Array<{ type: string; label: string }>
|
availableChips?: Array<{ type: string; label: string }>
|
||||||
@@ -361,6 +395,7 @@ defineEmits([
|
|||||||
'sign-out',
|
'sign-out',
|
||||||
'sign-in',
|
'sign-in',
|
||||||
'switch-team',
|
'switch-team',
|
||||||
|
'switch-role',
|
||||||
// Search events
|
// Search events
|
||||||
'start-select',
|
'start-select',
|
||||||
'cancel-select',
|
'cancel-select',
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
:theme="theme"
|
:theme="theme"
|
||||||
:user-data="userData"
|
:user-data="userData"
|
||||||
:is-seller="isSeller"
|
:is-seller="isSeller"
|
||||||
|
:has-buyer-team="hasBuyerTeam"
|
||||||
|
:has-seller-team="hasSellerTeam"
|
||||||
|
:current-role="currentRole"
|
||||||
:active-tokens="activeTokens"
|
:active-tokens="activeTokens"
|
||||||
:available-chips="availableChips"
|
:available-chips="availableChips"
|
||||||
:select-mode="selectMode"
|
:select-mode="selectMode"
|
||||||
@@ -36,6 +39,7 @@
|
|||||||
@sign-out="onClickSignOut"
|
@sign-out="onClickSignOut"
|
||||||
@sign-in="signIn()"
|
@sign-in="signIn()"
|
||||||
@switch-team="switchToTeam"
|
@switch-team="switchToTeam"
|
||||||
|
@switch-role="switchToRole"
|
||||||
@start-select="startSelect"
|
@start-select="startSelect"
|
||||||
@cancel-select="cancelSelect"
|
@cancel-select="cancelSelect"
|
||||||
@edit-token="editFilter"
|
@edit-token="editFilter"
|
||||||
@@ -129,7 +133,7 @@ const userData = useState<{
|
|||||||
avatarId?: string
|
avatarId?: string
|
||||||
activeTeam?: { name?: string; teamType?: string; logtoOrgId?: string; selectedLocation?: SelectedLocation | null }
|
activeTeam?: { name?: string; teamType?: string; logtoOrgId?: string; selectedLocation?: SelectedLocation | null }
|
||||||
activeTeamId?: string
|
activeTeamId?: string
|
||||||
teams?: Array<{ id?: string; name?: string; logtoOrgId?: string }>
|
teams?: Array<{ id?: string; name?: string; logtoOrgId?: string; teamType?: string }>
|
||||||
} | null>('me', () => null)
|
} | null>('me', () => null)
|
||||||
|
|
||||||
const sessionChecked = ref(false)
|
const sessionChecked = ref(false)
|
||||||
@@ -140,6 +144,19 @@ const isSeller = computed(() => {
|
|||||||
return userData.value?.activeTeam?.teamType === 'SELLER'
|
return userData.value?.activeTeam?.teamType === 'SELLER'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Role switching support
|
||||||
|
const buyerTeam = computed(() =>
|
||||||
|
userData.value?.teams?.find(t => t?.teamType === 'BUYER')
|
||||||
|
)
|
||||||
|
const sellerTeam = computed(() =>
|
||||||
|
userData.value?.teams?.find(t => t?.teamType === 'SELLER')
|
||||||
|
)
|
||||||
|
const hasBuyerTeam = computed(() => !!buyerTeam.value)
|
||||||
|
const hasSellerTeam = computed(() => !!sellerTeam.value)
|
||||||
|
const currentRole = computed(() =>
|
||||||
|
userData.value?.activeTeam?.teamType || 'BUYER'
|
||||||
|
)
|
||||||
|
|
||||||
const isLoggedIn = computed(() => loggedIn.value || !!userData.value?.id)
|
const isLoggedIn = computed(() => loggedIn.value || !!userData.value?.id)
|
||||||
|
|
||||||
const userName = computed(() => {
|
const userName = computed(() => {
|
||||||
@@ -252,7 +269,7 @@ watch(userData, () => {
|
|||||||
await fetchSession().catch(() => {})
|
await fetchSession().catch(() => {})
|
||||||
sessionChecked.value = true
|
sessionChecked.value = true
|
||||||
|
|
||||||
const switchToTeam = async (team: { id?: string; logtoOrgId?: string; name?: string }) => {
|
const switchToTeam = async (team: { id?: string; logtoOrgId?: string; name?: string; teamType?: string }) => {
|
||||||
if (!team?.id) return
|
if (!team?.id) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -271,6 +288,20 @@ const switchToTeam = async (team: { id?: string; logtoOrgId?: string; name?: str
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const switchToRole = async (role: 'BUYER' | 'SELLER') => {
|
||||||
|
const targetTeam = role === 'SELLER' ? sellerTeam.value : buyerTeam.value
|
||||||
|
if (targetTeam?.id) {
|
||||||
|
await switchToTeam(targetTeam)
|
||||||
|
// Redirect to appropriate page when in client area
|
||||||
|
if (isClientArea.value) {
|
||||||
|
const targetPath = role === 'SELLER'
|
||||||
|
? '/clientarea/offers'
|
||||||
|
: '/clientarea/orders'
|
||||||
|
await navigateTo(localePath(targetPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onClickSignOut = () => {
|
const onClickSignOut = () => {
|
||||||
signOut(siteUrl)
|
signOut(siteUrl)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
"seller": "Seller",
|
"seller": "Seller",
|
||||||
"suppliers": "Suppliers",
|
"suppliers": "Suppliers",
|
||||||
"hubs": "Hubs",
|
"hubs": "Hubs",
|
||||||
"ai": "AI assistant"
|
"ai": "AI assistant",
|
||||||
|
"roles": {
|
||||||
|
"client": "I'm a client",
|
||||||
|
"seller": "I'm a seller"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
"seller": "Продавец",
|
"seller": "Продавец",
|
||||||
"suppliers": "Поставщики",
|
"suppliers": "Поставщики",
|
||||||
"hubs": "Хабы",
|
"hubs": "Хабы",
|
||||||
"ai": "AI ассистент"
|
"ai": "AI ассистент",
|
||||||
|
"roles": {
|
||||||
|
"client": "Я клиент",
|
||||||
|
"seller": "Я продавец"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user