diff --git a/app/components/navigation/AreaTabs.vue b/app/components/navigation/AreaTabs.vue index 6b69482..1b4e892 100644 --- a/app/components/navigation/AreaTabs.vue +++ b/app/components/navigation/AreaTabs.vue @@ -2,6 +2,7 @@ import { Wrench, FlaskConical, Ticket, Sparkles, GraduationCap, Home } from 'lucide-vue-next' import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Badge } from '@/components/ui/badge' +import confetti from 'canvas-confetti' type RoleCode = 'private' | 'educator' | 'company' @@ -104,11 +105,77 @@ const currentArea = computed(() => { return matchedArea?.id || '' }) +// Track previous area IDs for animation detection +const previousAreaIds = ref([]) +const newlyAddedAreaId = ref(null) +const tabRefs = ref>({}) + +// Watch for changes in visible areas to trigger animations +watch(visibleAreas, (newAreas, oldAreas) => { + const newAreaIds = newAreas.map(a => a.id) + const oldAreaIds = oldAreas?.map(a => a.id) || [] + + // Find newly added areas + const addedIds = newAreaIds.filter(id => !oldAreaIds.includes(id)) + + if (addedIds.length > 0) { + // Mark as newly added for highlight animation + newlyAddedAreaId.value = addedIds[0] + + // Trigger confetti after a small delay (so element is rendered) + setTimeout(() => { + const areaId = addedIds[0] + const element = tabRefs.value[areaId] + if (element) { + triggerConfetti(element) + } + }, 300) + + // Clear highlight after animation + setTimeout(() => { + newlyAddedAreaId.value = null + }, 2000) + } + + previousAreaIds.value = newAreaIds +}, { deep: true }) + +// Initialize previous area IDs +onMounted(() => { + previousAreaIds.value = visibleAreas.value.map(a => a.id) +}) + +// Confetti effect from element position +function triggerConfetti(element: HTMLElement) { + const rect = element.getBoundingClientRect() + const x = (rect.left + rect.width / 2) / window.innerWidth + const y = (rect.top + rect.height / 2) / window.innerHeight + + // Confetti burst from tab position + confetti({ + particleCount: 25, + spread: 50, + origin: { x, y }, + colors: ['#E91E85', '#FF6B9D', '#C77DFF', '#9D4EDD'], + ticks: 200, + gravity: 1.2, + scalar: 0.8, + }) +} + function navigateToArea(area: ProductArea) { if (area.enabled) { navigateTo(area.route) } } + +function setTabRef(areaId: string, el: any) { + if (el) { + // Extract the actual DOM element from Vue component instance + const domElement = el.$el || el + tabRefs.value[areaId] = domElement + } +}