Implement Role Reset on Logout and Enhance AreaTabs Logic
- Added a resetRoles function in useActiveRole to clear user roles upon logout, ensuring a clean state. - Updated UserMenu.vue to call resetRoles during logout for improved role management. - Enhanced AreaTabs.vue to support multiple newly added tabs with staggered confetti animations, improving user engagement. - Refactored logic to track newly added areas and their visibility, ensuring a smoother navigation experience.
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
|||||||
} from './ui/dropdown-menu'
|
} from './ui/dropdown-menu'
|
||||||
|
|
||||||
const { user, loggedIn, logout } = useAuth()
|
const { user, loggedIn, logout } = useAuth()
|
||||||
|
const { resetRoles } = useActiveRole()
|
||||||
|
|
||||||
// Extended user type with all profile fields
|
// Extended user type with all profile fields
|
||||||
type ExtendedUser = typeof user.value & {
|
type ExtendedUser = typeof user.value & {
|
||||||
@@ -45,6 +46,8 @@ const userInitials = computed(() => {
|
|||||||
async function handleLogout() {
|
async function handleLogout() {
|
||||||
try {
|
try {
|
||||||
await logout()
|
await logout()
|
||||||
|
// Reset role state to default (private, no roles)
|
||||||
|
resetRoles()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Logout error:', error)
|
console.error('Logout error:', error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,12 +56,21 @@ const areas: ProductArea[] = [
|
|||||||
route: '/experimenta',
|
route: '/experimenta',
|
||||||
roleVisibility: 'all',
|
roleVisibility: 'all',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'pedagogical-offers',
|
||||||
|
label: 'Pädagogische Angebote',
|
||||||
|
icon: GraduationCap,
|
||||||
|
enabled: true,
|
||||||
|
visible: true,
|
||||||
|
route: '/experimenta',
|
||||||
|
roleVisibility: ['educator'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'labs',
|
id: 'labs',
|
||||||
label: 'Labore',
|
label: 'Labore',
|
||||||
icon: FlaskConical,
|
icon: FlaskConical,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
visible: false,
|
visible: true,
|
||||||
badge: 'Demnächst',
|
badge: 'Demnächst',
|
||||||
route: '/labs',
|
route: '/labs',
|
||||||
roleVisibility: ['educator', 'company'],
|
roleVisibility: ['educator', 'company'],
|
||||||
@@ -107,7 +116,7 @@ const currentArea = computed(() => {
|
|||||||
|
|
||||||
// Track previous area IDs for animation detection
|
// Track previous area IDs for animation detection
|
||||||
const previousAreaIds = ref<string[]>([])
|
const previousAreaIds = ref<string[]>([])
|
||||||
const newlyAddedAreaId = ref<string | null>(null)
|
const newlyAddedAreaIds = ref<string[]>([])
|
||||||
const tabRefs = ref<Record<string, HTMLElement>>({})
|
const tabRefs = ref<Record<string, HTMLElement>>({})
|
||||||
|
|
||||||
// Watch for changes in visible areas to trigger animations
|
// Watch for changes in visible areas to trigger animations
|
||||||
@@ -115,25 +124,40 @@ watch(visibleAreas, (newAreas, oldAreas) => {
|
|||||||
const newAreaIds = newAreas.map(a => a.id)
|
const newAreaIds = newAreas.map(a => a.id)
|
||||||
const oldAreaIds = oldAreas?.map(a => a.id) || []
|
const oldAreaIds = oldAreas?.map(a => a.id) || []
|
||||||
|
|
||||||
|
// Check if current route is still accessible with new visible areas
|
||||||
|
const currentPath = route.path
|
||||||
|
if (currentPath !== '/') {
|
||||||
|
const isCurrentRouteStillVisible = newAreas.some(area =>
|
||||||
|
area.route !== '/' && currentPath.startsWith(area.route)
|
||||||
|
)
|
||||||
|
if (!isCurrentRouteStillVisible) {
|
||||||
|
// Delay navigation until fade-out animation completes (300ms + 50ms buffer)
|
||||||
|
setTimeout(() => {
|
||||||
|
navigateTo('/')
|
||||||
|
}, 350)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find newly added areas
|
// Find newly added areas
|
||||||
const addedIds = newAreaIds.filter(id => !oldAreaIds.includes(id))
|
const addedIds = newAreaIds.filter(id => !oldAreaIds.includes(id))
|
||||||
|
|
||||||
if (addedIds.length > 0) {
|
if (addedIds.length > 0) {
|
||||||
// Mark as newly added for highlight animation
|
// Mark ALL newly added tabs for highlight animation
|
||||||
newlyAddedAreaId.value = addedIds[0]
|
newlyAddedAreaIds.value = addedIds
|
||||||
|
|
||||||
// Trigger confetti after a small delay (so element is rendered)
|
// Trigger confetti for each new tab with staggered delay (wave effect)
|
||||||
|
addedIds.forEach((areaId, index) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const areaId = addedIds[0]
|
|
||||||
const element = tabRefs.value[areaId]
|
const element = tabRefs.value[areaId]
|
||||||
if (element) {
|
if (element) {
|
||||||
triggerConfetti(element)
|
triggerConfetti(element)
|
||||||
}
|
}
|
||||||
}, 300)
|
}, 300 + (index * 150)) // Stagger: 300ms, 450ms, 600ms, ...
|
||||||
|
})
|
||||||
|
|
||||||
// Clear highlight after animation
|
// Clear all highlights after animation
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
newlyAddedAreaId.value = null
|
newlyAddedAreaIds.value = []
|
||||||
}, 2000)
|
}, 2000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +213,7 @@ function setTabRef(areaId: string, el: any) {
|
|||||||
:value="area.id" :disabled="!area.enabled" :class="[
|
:value="area.id" :disabled="!area.enabled" :class="[
|
||||||
'gap-2 py-3 md:py-4 data-[state=active]:bg-accent data-[state=active]:text-white data-[state=active]:shadow-md transition-all duration-300',
|
'gap-2 py-3 md:py-4 data-[state=active]:bg-accent data-[state=active]:text-white data-[state=active]:shadow-md transition-all duration-300',
|
||||||
!area.enabled && 'opacity-50 cursor-not-allowed',
|
!area.enabled && 'opacity-50 cursor-not-allowed',
|
||||||
newlyAddedAreaId === area.id && 'tab-highlight',
|
newlyAddedAreaIds.includes(area.id) && 'tab-highlight',
|
||||||
]" @click="navigateToArea(area)">
|
]" @click="navigateToArea(area)">
|
||||||
<component :is="area.icon" class="h-4 w-4" />
|
<component :is="area.icon" class="h-4 w-4" />
|
||||||
<span>{{ area.label }}</span>
|
<span>{{ area.label }}</span>
|
||||||
@@ -217,7 +241,7 @@ function setTabRef(areaId: string, el: any) {
|
|||||||
? 'bg-accent text-white shadow-md'
|
? 'bg-accent text-white shadow-md'
|
||||||
: 'text-white/70 hover:text-white',
|
: 'text-white/70 hover:text-white',
|
||||||
!area.enabled && 'opacity-50 cursor-not-allowed',
|
!area.enabled && 'opacity-50 cursor-not-allowed',
|
||||||
newlyAddedAreaId === area.id && 'tab-highlight',
|
newlyAddedAreaIds.includes(area.id) && 'tab-highlight',
|
||||||
]" @click="navigateToArea(area)">
|
]" @click="navigateToArea(area)">
|
||||||
<component :is="area.icon" class="h-4 w-4" />
|
<component :is="area.icon" class="h-4 w-4" />
|
||||||
<span>{{ area.label }}</span>
|
<span>{{ area.label }}</span>
|
||||||
|
|||||||
@@ -105,6 +105,17 @@ export function useActiveRole() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all role state (called on logout)
|
||||||
|
*/
|
||||||
|
function resetRoles() {
|
||||||
|
activeRole.value = 'private'
|
||||||
|
roles.value = []
|
||||||
|
roleChangedByAdmin.value = false
|
||||||
|
loading.value = false
|
||||||
|
error.value = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get roles that user actually has (approved only)
|
* Get roles that user actually has (approved only)
|
||||||
*/
|
*/
|
||||||
@@ -118,13 +129,21 @@ export function useActiveRole() {
|
|||||||
/**
|
/**
|
||||||
* Auto-fetch roles when user logs in
|
* Auto-fetch roles when user logs in
|
||||||
* This ensures the role button shows the correct role immediately after login
|
* This ensures the role button shows the correct role immediately after login
|
||||||
* Uses callOnce to prevent redundant API calls
|
|
||||||
*/
|
*/
|
||||||
const { loggedIn } = useUserSession()
|
const { loggedIn } = useUserSession()
|
||||||
|
|
||||||
|
// Initial fetch if already logged in
|
||||||
if (loggedIn.value) {
|
if (loggedIn.value) {
|
||||||
callOnce('init-roles', () => fetchRoleStatus())
|
callOnce('init-roles', () => fetchRoleStatus())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watch for login changes - fetch roles when user logs in
|
||||||
|
watch(loggedIn, (newLoggedIn) => {
|
||||||
|
if (newLoggedIn) {
|
||||||
|
fetchRoleStatus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State (useState already returns writable refs)
|
// State (useState already returns writable refs)
|
||||||
activeRole,
|
activeRole,
|
||||||
@@ -138,5 +157,6 @@ export function useActiveRole() {
|
|||||||
// Actions
|
// Actions
|
||||||
fetchRoleStatus,
|
fetchRoleStatus,
|
||||||
switchRole,
|
switchRole,
|
||||||
|
resetRoles,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
export function useAuth() {
|
export function useAuth() {
|
||||||
const { loggedIn, user, clear, fetch } = useUserSession()
|
const { loggedIn, user, clear, fetch } = useUserSession()
|
||||||
|
const { fetchRoleStatus } = useActiveRole()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Login with email and password
|
* Login with email and password
|
||||||
@@ -37,6 +38,9 @@ export function useAuth() {
|
|||||||
// Refresh user session
|
// Refresh user session
|
||||||
await fetch()
|
await fetch()
|
||||||
|
|
||||||
|
// Fetch user roles immediately after login
|
||||||
|
await fetchRoleStatus()
|
||||||
|
|
||||||
// Redirect to products page or saved destination
|
// Redirect to products page or saved destination
|
||||||
const redirectAfterLogin = useCookie('redirect_after_login')
|
const redirectAfterLogin = useCookie('redirect_after_login')
|
||||||
const destination = redirectAfterLogin.value || '/'
|
const destination = redirectAfterLogin.value || '/'
|
||||||
|
|||||||
Reference in New Issue
Block a user