Lewati ke isi

Breadcrumbs Best Practice - Scola FE v2

Overview

Breadcrumbs harus hanya ada di topbar, bukan di content area halaman. Ini adalah best practice UI/UX yang memastikan navigasi konsisten di seluruh aplikasi.

❌ Anti-Pattern (JANGAN LAKUKAN INI)

<!-- SALAH: Breadcrumbs di content area -->
<template>
  <AppLayout>
    <div class="px-4 py-5">
      <!-- ❌ Breadcrumbs di sini SALAH -->
      <AppBreadcrumbs 
        :items="[
          { label: 'Home', href: '/home' },
          { label: 'Current Page', active: true }
        ]" 
        class="mb-4" 
      />

      <h1>Page Title</h1>
      <!-- content -->
    </div>
  </AppLayout>
</template>

Masalah: - Breadcrumbs muncul 2x (topbar + content) - Tidak konsisten dengan halaman lain - Membuang space vertikal - Sulit maintenance (harus update di banyak tempat)

✅ Best Practice (LAKUKAN INI)

1. Topbar Otomatis (Menu-Based)

Breadcrumbs otomatis di-generate dari menu registry. Tidak perlu kode tambahan di halaman.

<!-- BENAR: Tidak ada breadcrumbs di content -->
<template>
  <AppLayout>
    <div class="px-4 py-5">
      <!-- Langsung ke header, no breadcrumbs -->
      <h1>Page Title</h1>
      <!-- content -->
    </div>
  </AppLayout>
</template>

Cara kerja: 1. Topbar.vue otomatis render breadcrumbs 2. getBreadcrumbs() dari useMenuV2 mencari route di menu registry 3. Generate breadcrumb trail dari menu hierarchy 4. Semua breadcrumb item dapat diklik (kecuali halaman aktif/saat ini) 5. Halaman saat ini selalu ditampilkan di breadcrumb trail

2. Konfigurasi Menu Registry

Untuk breadcrumbs otomatis bekerja, route harus terdaftar di menuRegistry.js:

// src/config/menuRegistry.js
{
  id: "library.catalog.view",
  label: "Perpustakaan",
  route: "/library/home",
  capabilityKey: "library.catalog.view",
  icon: Library,
  order: 1,
  // ✅ PENTING: Tambahkan relatedRoutes untuk sub-pages
  relatedRoutes: [
    "/library/catalog",
    "/library/reservations",
    "/library/loans",
    "/library/wishlist",
    "/library/reading-log",
    "/library/purchase-requests"
  ],
}

Hasil breadcrumbs: - /library/homeSekolah > Perpustakaan - /library/catalogSekolah > Perpustakaan > Katalog ✅ (current page shown) - /library/loansSekolah > Perpustakaan > Daftar Pinjaman ✅ (current page shown)

Navigasi: - Sekolah → Clickable, navigasi ke home - Perpustakaan → Clickable, navigasi ke /library/home - KatalogNot clickable (current page, shown in bold)

3. Custom Breadcrumbs (Untuk Halaman Detail)

Jika perlu custom breadcrumbs (misal: halaman detail dengan data dinamis):

<script setup>
import { useBreadcrumbs } from '@/composables/useBreadcrumbs';
import { onMounted, onUnmounted } from 'vue';

const { setBreadcrumbs, clearBreadcrumbs } = useBreadcrumbs();

onMounted(async () => {
  // Fetch data dulu
  const student = await fetchStudent(id);

  // Set custom breadcrumbs
  setBreadcrumbs([
    { label: 'Data Siswa', href: '/students' },
    { label: student.name, active: true }
  ]);
});

onUnmounted(() => {
  clearBreadcrumbs(); // Optional, auto-clear saat route berubah
});
</script>

Implementasi Pattern

Step 1: Hapus Breadcrumbs dari Content

# Cari semua file dengan AppBreadcrumbs di content area
grep -r "AppBreadcrumbs" src/views/

Untuk setiap file: 1. Hapus <AppBreadcrumbs /> component dari template 2. Hapus import AppBreadcrumbs dari script 3. Hapus breadcrumb items array jika ada

Step 2: Update Menu Registry

Untuk setiap menu entry yang punya sub-pages:

{
  id: "menu.parent",
  label: "Parent Menu",
  route: "/parent",
  // Tambahkan relatedRoutes untuk semua sub-pages
  relatedRoutes: [
    "/parent/sub1",
    "/parent/sub2",
    "/parent/sub3"
  ],
}

Step 3: Verifikasi

  1. Buka halaman di browser
  2. Cek breadcrumbs di topbar (bukan di content)
  3. Navigasi ke sub-pages, breadcrumbs harus update otomatis
  4. Tidak ada breadcrumbs duplikat

Troubleshooting

Penyebab: Route tidak terdaftar di menu registry

Solusi: 1. Cek menuRegistry.js untuk role yang sesuai 2. Pastikan route ada di menu entry atau relatedRoutes 3. Pastikan user punya capability yang sesuai

Penyebab: relatedRoutes tidak lengkap atau salah

Solusi: 1. Tambahkan semua sub-routes ke relatedRoutes array 2. Pastikan path exact match (case-sensitive) 3. Gunakan absolute path, bukan relative

Penyebab: Custom breadcrumbs tidak di-clear

Solusi:

// Pastikan clear di onUnmounted
onUnmounted(() => {
  clearBreadcrumbs();
});

// Atau gunakan watcher di useBreadcrumbs (sudah otomatis)

Migration Checklist

Untuk migrate existing pages:

  • [ ] Hapus <AppBreadcrumbs /> dari template
  • [ ] Hapus import AppBreadcrumbs dari script
  • [ ] Hapus breadcrumb items array/data
  • [ ] Update menuRegistry.js dengan relatedRoutes
  • [ ] Test breadcrumbs di topbar
  • [ ] Verify navigation flow
  • [ ] Commit dengan message yang jelas

Contoh Implementasi

Before (❌ Anti-pattern)

<template>
  <AppLayout>
    <div class="px-4 py-5">
      <AppBreadcrumbs 
        :items="[
          { label: 'Perpustakaan', href: '/library/home' },
          { label: 'Katalog', active: true }
        ]" 
        class="mb-4" 
      />
      <h1>Katalog Perpustakaan</h1>
    </div>
  </AppLayout>
</template>

<script setup>
import AppBreadcrumbs from '@/components/ui/AppBreadcrumbs.vue';
</script>

After (✅ Best practice)

<template>
  <AppLayout>
    <div class="px-4 py-5">
      <!-- No breadcrumbs here! -->
      <h1>Katalog Perpustakaan</h1>
    </div>
  </AppLayout>
</template>

<script setup>
// No AppBreadcrumbs import needed
</script>
// menuRegistry.js
{
  id: "library.catalog.view",
  label: "Perpustakaan",
  route: "/library/home",
  relatedRoutes: ["/library/catalog"], // ✅ Added
}

How Breadcrumbs Work as Navigation

Smart Breadcrumb Generation

getBreadcrumbs() in useMenuV2.js intelligently generates breadcrumb trails:

  1. Exact Match: Route directly in menu → Simple trail

    /library/home → Sekolah > Perpustakaan
    

  2. Related Route Match: Route in relatedRoutes → Enhanced trail

    /library/catalog → Sekolah > Perpustakaan > Katalog
    

Process: - Match parent via relatedRoutes - Check if child exists in menu - If not, create breadcrumb from routeMap - Add to trail: parent + current page

  1. Route Label Mapping: For routes not in menu structure
    const routeMap = {
      '/library/catalog': 'Katalog',
      '/library/loans': 'Daftar Pinjaman',
      '/library/wishlist': 'Daftar Keinginan',
      // etc.
    };
    

Clickable Navigation

All breadcrumb items are clickable except the current page:

Home > Perpustakaan > Katalog
 ✓        ✓            ✗
click    click      (current)

AppBreadcrumbs.vue handles: - Blue links for clickable items (href provided) - Bold text for current page (active: true) - Proper routing via router.push()

Key Properties

Breadcrumb items have: - label: Display text - href: Navigation target (null for current page) - active: Boolean (true for current page)

Example:

[
  { label: 'Sekolah', href: '/faculty/home', active: false },
  { label: 'Perpustakaan', href: '/library/home', active: false },
  { label: 'Katalog', href: null, active: true }
]

Benefits

  1. Konsistensi: Breadcrumbs selalu di tempat yang sama (topbar)
  2. Maintainability: Single source of truth (menu registry)
  3. DRY: Tidak perlu duplicate breadcrumb logic di setiap halaman
  4. UX: Standard pattern yang familiar untuk users
  5. Performance: Tidak render breadcrumbs 2x
  6. Navigation: Breadcrumbs berfungsi sebagai alat navigasi yang sebenarnya
  7. Context: User selalu tahu posisi mereka di aplikasi

References

  • src/components/Topbar.vue - Topbar component dengan breadcrumbs
  • src/composables/useMenuV2.js - Menu system dengan getBreadcrumbs()
  • src/composables/useBreadcrumbs.js - Custom breadcrumbs composable
  • src/config/menuRegistry.js - Menu configuration
  • src/components/ui/AppBreadcrumbs.vue - Breadcrumbs component

Last Updated: 2026-03-01
Author: Scola Development Team