---
name: Router and Navigation
description: Hướng dẫn cấu hình Vue Router và navigation trong dự án LADA Express
---

# Router and Navigation Skill

## Mục đích

Skill này hướng dẫn cách cấu hình Vue Router, tạo routes, và implement navigation trong dự án LADA Express.

## Cấu trúc Router

```
src/
└── routers/
    ├── index.js          # Router configuration
    ├── routes.js         # Main routes file
    ├── auth.js           # Authentication routes
    ├── finance.js        # Finance module routes
    ├── order.js          # Order module routes
    ├── hr.js             # HR module routes
    └── ...
```

## 1. Router Configuration

### routers/index.js

```javascript
import { createRouter, createWebHistory } from 'vue-router';
import { routes } from './routes';
import { useUserStore } from '@/stores';

const router = createRouter({
  routes: routes,
  mode: 'history',
  history: createWebHistory(),
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition;
    } else {
      return { top: 0 };
    }
  }
});

// Navigation guard
router.beforeEach(async (to, from, next) => {
  const uStore = useUserStore();
  
  // Public pages không cần authentication
  const publicPages = [
    'login',
    'register',
    'forgot-password',
    'unauthorized'
  ];
  
  const authRequired = !publicPages.includes(to.name);
  
  // Set document title
  document.title = to.meta.title + ' | LADA Express';
  
  // Check authentication
  if (authRequired && !uStore.user) {
    uStore.returnUrl = to.fullPath;
    next({ name: 'login' });
  } else {
    // Check permissions
    if (to.meta.roles && to.meta.roles.length > 0) {
      const hasRole = to.meta.roles.some(role => uStore.hasRole(role));
      
      if (!hasRole) {
        next({ name: 'unauthorized' });
        return;
      }
    }
    
    next();
  }
});

export default router;
```

## 2. Routes Organization

### routers/routes.js

```javascript
import HomeView from '@/views/home/index.vue';

const NotFound = () => import('@/views/404.vue');
const Unauthorized = () => import('@/views/unauthorized.vue');

// Import route modules
import { AuthRoutes } from './auth';
import { FinanceRoutes } from './finance';
import { OrderRoutes } from './order';
import { HRRoutes } from './hr';
import { SystemRoutes } from './system';
import { ReportRoutes } from './reports';

export const routes = [
  ...AuthRoutes,
  ...FinanceRoutes,
  ...OrderRoutes,
  ...HRRoutes,
  ...SystemRoutes,
  ...ReportRoutes,
  
  {
    name: 'home',
    path: '/',
    component: HomeView,
    meta: {
      title: 'Trang chủ',
      icon: 'fe fe-home'
    }
  },
  
  {
    name: 'unauthorized',
    path: '/khong-co-quyen-truy-cap',
    component: Unauthorized,
    meta: {
      title: 'Không có quyền truy cập'
    }
  },
  
  {
    path: '/:catchAll(.*)',
    component: NotFound,
    meta: {
      title: 'Không tìm thấy'
    }
  }
];
```

### Module Routes (e.g., routers/finance.js)

```javascript
const FinanceIndex = () => import('@/views/finances/index.vue');
const ExpenseList = () => import('@/views/finances/expenses/index.vue');
const ExpenseForm = () => import('@/views/finances/expenses/form.vue');
const IncomeList = () => import('@/views/finances/incomes/index.vue');
const IncomeForm = () => import('@/views/finances/incomes/form.vue');

export const FinanceRoutes = [
  {
    name: 'finance',
    path: '/tai-chinh',
    component: FinanceIndex,
    meta: {
      title: 'Tài chính',
      icon: 'fe fe-dollar-sign',
      roles: ['Admin', 'Finance']
    }
  },
  {
    name: 'expense-list',
    path: '/tai-chinh/chi-phi',
    component: ExpenseList,
    meta: {
      title: 'Danh sách chi phí',
      breadcrumb: [
        { name: 'Trang chủ', path: '/' },
        { name: 'Tài chính', path: '/tai-chinh' },
        { name: 'Chi phí' }
      ],
      roles: ['Admin', 'Finance']
    }
  },
  {
    name: 'expense-create',
    path: '/tai-chinh/chi-phi/tao-moi',
    component: ExpenseForm,
    meta: {
      title: 'Tạo chi phí mới',
      breadcrumb: [
        { name: 'Trang chủ', path: '/' },
        { name: 'Tài chính', path: '/tai-chinh' },
        { name: 'Chi phí', path: '/tai-chinh/chi-phi' },
        { name: 'Tạo mới' }
      ],
      roles: ['Admin', 'Finance']
    }
  },
  {
    name: 'expense-edit',
    path: '/tai-chinh/chi-phi/:id/chinh-sua',
    component: ExpenseForm,
    props: true,
    meta: {
      title: 'Chỉnh sửa chi phí',
      breadcrumb: [
        { name: 'Trang chủ', path: '/' },
        { name: 'Tài chính', path: '/tai-chinh' },
        { name: 'Chi phí', path: '/tai-chinh/chi-phi' },
        { name: 'Chỉnh sửa' }
      ],
      roles: ['Admin', 'Finance']
    }
  }
];
```

## 3. Route Meta Fields

```javascript
{
  name: 'route-name',
  path: '/path',
  component: Component,
  meta: {
    title: 'Page Title',              // Document title
    icon: 'fe fe-icon',                // Menu icon
    roles: ['Admin', 'User'],          // Required roles
    breadcrumb: [...],                 // Breadcrumb items
    hideInMenu: false,                 // Hide from menu
    requiresAuth: true,                // Requires authentication
    layout: 'default'                  // Layout type
  }
}
```

## 4. Navigation trong Component

### Programmatic Navigation

```vue
<script setup>
import { useRouter, useRoute } from 'vue-router';

const router = useRouter();
const route = useRoute();

// Navigate to route
function goToList() {
  router.push({ name: 'expense-list' });
}

// Navigate with params
function goToEdit(id) {
  router.push({
    name: 'expense-edit',
    params: { id: id }
  });
}

// Navigate with query
function goToListWithFilter() {
  router.push({
    name: 'expense-list',
    query: { status: 'active', page: 1 }
  });
}

// Go back
function goBack() {
  router.back();
}

// Replace current route
function replaceRoute() {
  router.replace({ name: 'home' });
}

// Access current route
const currentId = route.params.id;
const currentQuery = route.query;
</script>
```

### Router Link

```vue
<template>
  <!-- Basic link -->
  <router-link to="/tai-chinh">Tài chính</router-link>
  
  <!-- Named route -->
  <router-link :to="{ name: 'expense-list' }">
    Danh sách chi phí
  </router-link>
  
  <!-- With params -->
  <router-link :to="{ name: 'expense-edit', params: { id: 123 } }">
    Chỉnh sửa
  </router-link>
  
  <!-- With query -->
  <router-link :to="{ name: 'expense-list', query: { status: 'active' } }">
    Chi phí đang hoạt động
  </router-link>
  
  <!-- With custom class -->
  <router-link 
    :to="{ name: 'home' }"
    class="btn btn-primary"
    active-class="active"
  >
    Trang chủ
  </router-link>
</template>
```

## 5. Route Guards

### Per-route Guard

```javascript
{
  name: 'admin-panel',
  path: '/admin',
  component: AdminPanel,
  beforeEnter: (to, from, next) => {
    const userStore = useUserStore();
    
    if (userStore.hasRole('Admin')) {
      next();
    } else {
      next({ name: 'unauthorized' });
    }
  }
}
```

### Component Guard

```vue
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router';

// Confirm before leaving
onBeforeRouteLeave((to, from, next) => {
  if (hasUnsavedChanges.value) {
    const answer = window.confirm('Bạn có thay đổi chưa lưu. Bạn có chắc muốn rời đi?');
    if (answer) {
      next();
    } else {
      next(false);
    }
  } else {
    next();
  }
});

// Update when params change
onBeforeRouteUpdate((to, from, next) => {
  // Load new data based on new params
  loadData(to.params.id);
  next();
});
</script>
```

## 6. Breadcrumb Component

```vue
<template>
  <div class="breadcrumb-header justify-content-between">
    <div class="my-auto">
      <h4 class="page-title">{{ pageTitle }}</h4>
      <ol class="breadcrumb">
        <li 
          v-for="(item, index) in breadcrumbs" 
          :key="index"
          class="breadcrumb-item"
          :class="{ active: index === breadcrumbs.length - 1 }"
        >
          <router-link 
            v-if="item.path && index !== breadcrumbs.length - 1" 
            :to="item.path"
          >
            {{ item.name }}
          </router-link>
          <span v-else>{{ item.name }}</span>
        </li>
      </ol>
    </div>
  </div>
</template>

<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';

const route = useRoute();

const pageTitle = computed(() => route.meta.title || '');

const breadcrumbs = computed(() => {
  return route.meta.breadcrumb || [
    { name: 'Trang chủ', path: '/' },
    { name: pageTitle.value }
  ];
});
</script>
```

## 7. Dynamic Routes

### Route với params

```javascript
{
  name: 'user-profile',
  path: '/nguoi-dung/:id',
  component: UserProfile,
  props: true  // Pass params as props
}
```

### Component nhận params

```vue
<script setup>
import { defineProps, onMounted } from 'vue';

const props = defineProps({
  id: {
    type: [String, Number],
    required: true
  }
});

onMounted(() => {
  console.log('User ID:', props.id);
});
</script>
```

## 8. Nested Routes

```javascript
{
  name: 'settings',
  path: '/cai-dat',
  component: SettingsLayout,
  children: [
    {
      name: 'settings-profile',
      path: 'ho-so',
      component: ProfileSettings
    },
    {
      name: 'settings-security',
      path: 'bao-mat',
      component: SecuritySettings
    },
    {
      name: 'settings-notifications',
      path: 'thong-bao',
      component: NotificationSettings
    }
  ]
}
```

## Best Practices

### 1. Route Naming

- Sử dụng kebab-case cho paths
- Sử dụng descriptive names
- Prefix với module name (e.g., `finance-expense-list`)

### 2. Lazy Loading

- Sử dụng dynamic imports cho routes
- Group related routes
- Reduce initial bundle size

### 3. Meta Fields

- Always set title
- Set roles cho protected routes
- Add breadcrumb cho better UX

### 4. Navigation

- Sử dụng named routes thay vì paths
- Handle navigation errors
- Provide feedback on navigation

## Checklist khi tạo Routes mới

- [ ] Tạo route file trong `routers/`
- [ ] Define routes với lazy loading
- [ ] Set meta fields (title, roles, breadcrumb)
- [ ] Export routes
- [ ] Import vào `routes.js`
- [ ] Test navigation
- [ ] Test authentication guard
- [ ] Test permission guard
- [ ] Update menu nếu cần
