--- description: Vue 3, TypeScript, and Vuexy patterns and conventions globs: ["apps/**/*.{vue,ts,tsx}"] alwaysApply: true --- # Vue + Vuexy Rules ## Core Principles 1. **Composition API only** - Use ` ``` ### Router Configuration ```typescript // src/router/index.ts import { createRouter, createWebHistory } from 'vue-router' import { useAuthStore } from '@/stores/auth' const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', component: () => import('@/layouts/default.vue'), children: [ { path: '', name: 'dashboard', component: () => import('@/pages/dashboard.vue'), meta: { requiresAuth: true }, }, { path: 'events', name: 'events', component: () => import('@/pages/events/index.vue'), meta: { requiresAuth: true }, }, { path: 'events/create', name: 'events-create', component: () => import('@/pages/events/create.vue'), meta: { requiresAuth: true }, }, { path: 'events/:id', name: 'events-show', component: () => import('@/pages/events/[id].vue'), meta: { requiresAuth: true }, }, { path: 'events/:id/edit', name: 'events-edit', component: () => import('@/pages/events/[id]/edit.vue'), meta: { requiresAuth: true }, }, // Add more routes... ], }, { path: '/login', name: 'login', component: () => import('@/pages/login.vue'), meta: { layout: 'blank', requiresAuth: false }, }, { path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/pages/[...error].vue'), }, ], }) // Navigation guard router.beforeEach(async (to, from, next) => { const authStore = useAuthStore() // Check if route requires auth if (to.meta.requiresAuth && !authStore.isAuthenticated) { // Try to fetch user if we have a token if (authStore.token) { const success = await authStore.fetchUser() if (success) { return next() } } return next({ name: 'login', query: { redirect: to.fullPath } }) } // Redirect to dashboard if logged in and going to login if (to.name === 'login' && authStore.isAuthenticated) { return next({ name: 'dashboard' }) } next() }) export default router ``` ### Navigation Menu ```typescript // src/navigation/vertical/index.ts import type { VerticalNavItems } from '@/@layouts/types' export default [ { title: 'Dashboard', to: { name: 'dashboard' }, icon: { icon: 'tabler-smart-home' }, }, { heading: 'Management', }, { title: 'Events', to: { name: 'events' }, icon: { icon: 'tabler-calendar-event' }, }, { title: 'Members', to: { name: 'members' }, icon: { icon: 'tabler-users' }, }, { title: 'Music', to: { name: 'music' }, icon: { icon: 'tabler-music' }, }, { title: 'Setlists', to: { name: 'setlists' }, icon: { icon: 'tabler-playlist' }, }, { heading: 'CRM', }, { title: 'Locations', to: { name: 'locations' }, icon: { icon: 'tabler-map-pin' }, }, { title: 'Customers', to: { name: 'customers' }, icon: { icon: 'tabler-building' }, }, ] as VerticalNavItems ``` ## Best Practices ### Always Use - ` ``` ## Event Handlers Name handlers with `handle` prefix: ```vue ``` ## File Organization ``` src/ ├── @core/ # Vuexy core (DON'T MODIFY) ├── @layouts/ # Vuexy layouts (DON'T MODIFY) ├── assets/ # Static assets │ ├── images/ │ └── styles/ ├── components/ # Shared components │ ├── ui/ # Base UI components │ └── features/ # Feature-specific components ├── composables/ # Custom composables │ ├── useEvents.ts │ ├── useMembers.ts │ └── useAuth.ts ├── layouts/ # App layouts ├── lib/ # Utilities │ ├── api-client.ts │ ├── utils.ts │ └── query-client.ts ├── navigation/ # Menu configuration ├── pages/ # Route pages │ ├── dashboard.vue │ ├── events/ │ │ ├── index.vue │ │ ├── create.vue │ │ └── [id]/ │ │ ├── index.vue │ │ └── edit.vue │ └── login.vue ├── plugins/ # Vue plugins ├── router/ # Vue Router ├── stores/ # Pinia stores ├── types/ # TypeScript types │ └── index.ts └── App.vue ``` ## Performance ### Lazy Load Routes ```typescript // ✅ Good - lazy load all route components const routes = [ { path: '/events', component: () => import('@/pages/events/index.vue'), }, ] ``` ### Use Computed for Derived State ```typescript // ✅ Good - computed is cached const upcomingEvents = computed(() => events.value.filter(e => new Date(e.event_date) > new Date()) ) // ❌ Avoid - recalculates on every render const upcomingEvents = events.value.filter(e => new Date(e.event_date) > new Date()) ``` ### Prefetch on Hover ```typescript // Prefetch event details when user hovers function handleEventHover(id: string) { queryClient.prefetchQuery({ queryKey: ['events', id], queryFn: () => fetchEvent(id), }) } ``` ### Use v-once for Static Content ```vue

Welcome to Band Management

Manage your band operations efficiently.

``` ## Error Handling ```vue ``` ## API Integration Pattern Create typed API functions: ```typescript // lib/api/events.ts import { apiClient } from '@/lib/api-client' import type { Event, CreateEventData, ApiResponse } from '@/types' export const eventsApi = { list: async (params?: { page?: number; status?: string }) => { const { data } = await apiClient.get>('/events', { params }) return data }, get: async (id: string) => { const { data } = await apiClient.get>(`/events/${id}`) return data.data }, create: async (eventData: CreateEventData) => { const { data } = await apiClient.post>('/events', eventData) return data.data }, update: async (id: string, eventData: Partial) => { const { data } = await apiClient.put>(`/events/${id}`, eventData) return data.data }, delete: async (id: string) => { await apiClient.delete(`/events/${id}`) }, } ```