diff --git a/.env b/.env index e963575..02f5a50 100644 --- a/.env +++ b/.env @@ -11,7 +11,7 @@ LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 -DB_DATABASE=academy_lite_1.0 +DB_DATABASE=db_courses DB_USERNAME=root DB_PASSWORD= diff --git a/.gitignore b/.gitignore index 66976f7..a558032 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /resources/views/components/home_made_by_builder /storage/*.key /vendor +.env .env.backup .env.production .phpunit.result.cache @@ -27,4 +28,4 @@ yarn-error.log # Ignore macOS .DS_Store files .DS_Store -/config/database.php \ No newline at end of file +/config/database.php diff --git a/public/assets/frontend/default/css/top-navbar.css b/public/assets/frontend/default/css/top-navbar.css new file mode 100644 index 0000000..9126506 --- /dev/null +++ b/public/assets/frontend/default/css/top-navbar.css @@ -0,0 +1,314 @@ +/* ===== WRAPPER UTAMA NAVBAR ===== */ + +#top_menu { + background-color: #1f2937; + border-bottom: 1px solid rgba(255, 255, 255, .07); + color: #f9fafb; + min-height: 56px; + display: flex; + align-items: center; + gap: 8px; + padding: 0 8px; + position: relative; + z-index: 1000; + overflow: visible; +} + + +/* viewport horizontal scroll */ + +.topnav-scroll-outer { + flex: 1 1 auto; + overflow: hidden; + /* sembunyikan yang kepotong kanan/kiri */ + position: relative; +} + + +/* konten yang digeser */ + +.topnav-scroll-inner { + display: flex; + transition: transform 0.25s ease; + will-change: transform; +} + + +/* nav yg di-clone */ + +#topnav-nav.topnav-nav { + /* hapus width:100%; + karena sekarang dia inline dalam flex */ + display: flex; +} + + +/* root UL (level 1 list) = flex row */ + +#topnav-nav .topnav-root { + display: flex; + align-items: center; + gap: 16px; + margin: 0; + padding: 0; + list-style: none; + white-space: nowrap; +} + + +/* tombol panah kiri/kanan */ + +.topnav-scroll-btn { + flex: 0 0 auto; + background: rgba(0, 0, 0, 0.4); + color: #fff; + border: 1px solid rgba(255, 255, 255, .15); + border-radius: 6px; + font-size: 12px; + line-height: 1; + height: 28px; + min-width: 28px; + padding: 0 6px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + user-select: none; +} + +.topnav-scroll-btn:hover { + background: rgba(255, 255, 255, 0.08); +} + +.topnav-scroll-btn[disabled] { + opacity: .3; + cursor: default; +} + + +/* ===== LEVEL 1 ITEM ===== */ + +#topnav-nav .topnav-item { + position: relative; + display: flex; + align-items: center; + gap: 8px; + font-size: 14px; + font-weight: 500; + line-height: 1; + color: #e5e7eb; + padding: 10px 12px; + border-radius: 8px; + cursor: pointer; + white-space: nowrap; +} + +#topnav-nav .topnav-item>a { + display: flex; + align-items: center; + gap: 8px; + color: inherit; + text-decoration: none; + line-height: 1; +} + + +/* state hover / active di level 1 */ + +#topnav-nav .topnav-item:hover { + background-color: rgba(255, 255, 255, .08); + color: #fff; +} + + +/* aktif berdasarkan sidebar-first-li.active */ + +#topnav-nav .topnav-item.is-active { + background-color: rgba(96, 165, 250, .18); + /* biru lembut */ + color: #fff; + box-shadow: 0 0 0 1px rgba(147, 197, 253, .4) inset; +} + +#topnav-nav .topnav-icon { + font-size: 16px; + line-height: 1; + display: flex; + align-items: center; + color: #9ca3af; +} + +#topnav-nav .topnav-item:hover .topnav-icon, +#topnav-nav .topnav-item.is-active .topnav-icon { + color: #fff; +} + + +/* ===== tanda panah kecil di item yg punya submenu (POINT #2) ===== */ + + +/* Kita tempelkan panah ini setelah label text di level 1 */ + +#topnav-nav .topnav-item.has-sub>.topnav-mainlabel::after { + content: "\f130"; + font-family: uicons-regular-rounded !important; + font-style: normal; + font-weight: normal !important; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + background-image: unset; + margin-top: 1.5px; + margin-left: 4px; + font-size: 12px; + color: #9ca3af; +} + +#topnav-nav .topnav-item.has-sub:hover>.topnav-mainlabel::after, +#topnav-nav .topnav-item.is-active.has-sub>.topnav-mainlabel::after { + color: #fff; +} + + +/* ===== DROPDOWN LEVEL 2 ===== */ + +#topnav-nav .topnav-dropdown { + position: absolute; + top: 100%; + left: 0; + min-width: 220px; + background-color: #111827; + border: 1px solid rgba(255, 255, 255, .07); + border-radius: 10px; + box-shadow: 0 24px 48px -12px rgba(0, 0, 0, .9), 0 0 4px rgba(255, 255, 255, .08) inset; + padding: 12px 0; + z-index: 1200; + list-style: none; + margin: 0; + display: none; +} + + +/* judul group */ + +#topnav-nav .topnav-group-title { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .05em; + color: #6b7280; + padding: 8px 16px 4px 16px; + line-height: 1.2; +} + + +/* item level 2 */ + +#topnav-nav .topnav-subitem { + position: relative; + list-style: none; + margin: 0; + padding: 0; +} + +#topnav-nav .topnav-subitem>a { + display: block; + font-size: 13px; + font-weight: 500; + line-height: 1.3; + color: #e5e7eb; + text-decoration: none; + padding: 8px 16px; + white-space: nowrap; + cursor: pointer; + background: transparent; +} + + +/* hover level 2 */ + +#topnav-nav .topnav-subitem>a:hover { + background-color: rgba(255, 255, 255, .06); + color: #fff; +} + + +/* ACTIVE di level 2 (POINT #3) */ + +#topnav-nav .topnav-subitem.is-active>a { + background-color: rgba(96, 165, 250, .18); + color: #fff; + box-shadow: 0 0 0 1px rgba(147, 197, 253, .4) inset; +} + + +/* tanda panah untuk level 2 yg punya anak */ + +#topnav-nav .topnav-subitem.has-sub2>a::after { + content: '›'; + float: right; + color: #6b7280; + font-size: 12px; + line-height: 1; +} + + +/* ===== DROPDOWN LEVEL 3 ===== */ + +#topnav-nav .topnav-subdropdown { + position: absolute; + top: 0; + left: 100%; + min-width: 220px; + background-color: #1f2937; + border: 1px solid rgba(255, 255, 255, .07); + border-radius: 10px; + box-shadow: 0 24px 48px -12px rgba(0, 0, 0, .9), 0 0 4px rgba(255, 255, 255, .08) inset; + padding: 12px 0; + z-index: 1300; + list-style: none; + margin: 0; + display: none; +} + + +/* item level 3 */ + +#topnav-nav .topnav-subsubitem>a { + display: block; + padding: 8px 16px; + font-size: 13px; + font-weight: 400; + line-height: 1.3; + color: #d1d5db; + text-decoration: none; + white-space: nowrap; + background: transparent; +} + +#topnav-nav .topnav-subsubitem>a:hover { + background-color: rgba(255, 255, 255, .07); + color: #fff; +} + + +/* aktif level 3 optional (kalau mau deteksi aktif di situ juga) */ + +#topnav-nav .topnav-subsubitem.is-active>a { + background-color: rgba(96, 165, 250, .18); + color: #fff; + box-shadow: 0 0 0 1px rgba(147, 197, 253, .4) inset; +} + + +/* responsive mobile tetap sama ide dasarnya, tapi tombol scroll biasanya disembunyikan */ + +@media (max-width:768px) { + .topnav-scroll-btn { + display: none !important; + } + #top_menu { + min-height: 50px; + } +} \ No newline at end of file diff --git a/public/assets/frontend/default/js/top-navbar.js b/public/assets/frontend/default/js/top-navbar.js new file mode 100644 index 0000000..0985be7 --- /dev/null +++ b/public/assets/frontend/default/js/top-navbar.js @@ -0,0 +1,142 @@ +$(function() { + // 1. Pastikan #top_menu ada di lokasi yang kamu mau + if ($(".ol-sidebar-content").length && $("#top_menu").length === 0) { + $(".ol-sidebar-content").prepend('
'); + } + + // 2. Ambil nav sidebar asli + const $origNav = $(".ol-sidebar .sidebar-nav").first(); + const $clone = $origNav.clone(true, true); + + // 3. Bersihkan & ubah class utk versi topnav + $clone + .removeClass() // hapus "sidebar-nav" + .attr("id", "topnav-nav") + .addClass("topnav-nav"); + + // ubah UL root + $clone.find("> ul, > .px-14px, ul.px-14px").each(function() { + $(this).removeClass().addClass("topnav-root"); + }); + + // ===== LEVEL 1 (sidebar-first-li) -> .topnav-item + $clone.find("li.sidebar-first-li").each(function() { + const $li = $(this); + + const hasSub = $li.find("ul.first-sub-menu").length > 0; + const isActive = $li.hasClass("active"); // <== gunakan langsung class active asli + + $li + .removeClass("sidebar-first-li first-li-have-sub active showMenu") + .addClass("topnav-item") + .toggleClass("has-sub", hasSub) + .toggleClass("is-active", isActive); + + // Ambil asli supaya kita bisa buat struktur icon + label + const $a = $li.children("a").first(); + if ($a.length) { + const $icon = $a.find(".icon").first(); + const $textNode = $a.find(".text span").first(); //