From d3af134696ac013810632620c7380123da708314 Mon Sep 17 00:00:00 2001 From: Teal Bauer <teal@starsong.eu> Date: Mon, 10 Mar 2025 00:24:29 +0100 Subject: [PATCH] Add mobile hamburger menu(s) --- app/assets/stylesheets/application.css | 66 +++++++++++++ .../stylesheets/application.tailwind.css | 26 +---- .../admin/conferences/import_history.html.erb | 6 +- app/views/admin/conferences/index.html.erb | 10 +- app/views/admin/conferences/show.html.erb | 6 +- app/views/admin/dashboard/index.html.erb | 26 ++--- app/views/admin/roles/_form.html.erb | 10 +- app/views/admin/roles/edit.html.erb | 2 +- app/views/admin/roles/index.html.erb | 4 +- app/views/admin/users/index.html.erb | 10 +- app/views/conferences/show.html.erb | 4 +- app/views/layouts/application.html.erb | 93 ++++++++++++++++-- app/views/shared/_admin_nav.html.erb | 95 +++++++++++++++++-- 13 files changed, 279 insertions(+), 79 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 3fa38dc..3900c2b 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -52,11 +52,77 @@ .stages { display: flex; flex-direction: row; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; + min-width: 100%; } .stages .stage { width: 400px; + min-width: 300px; padding: 0 8px; border-right: 1px dashed gray; + flex-shrink: 0; +} + +/* Improve mobile scrolling for tables */ +.table-responsive, +table.w-full, +.overflow-x-auto, +.admin-table-container, +div:has(> table) { + width: 100%; + overflow-x: auto !important; + -webkit-overflow-scrolling: touch; + max-width: 100vw; + display: block; +} + +/* Mobile responsive headers with buttons */ +@media (max-width: 640px) { + .header-with-actions { + flex-direction: column; + align-items: flex-start; + } + + .header-with-actions h1, + .header-with-actions h2, + .header-with-actions h3 { + margin-bottom: 0.75rem; + } + + .header-with-actions .actions { + margin-top: 0.5rem; + margin-bottom: 1rem; + } +} + +/* Make day headers sticky on mobile devices */ +.conference-day h3.sticky { + position: sticky; + top: 0; + z-index: 40; + backdrop-filter: blur(4px); +} + +/* Responsive admin dashboard tiles */ +@media (max-width: 768px) { + .admin-dashboard-tiles { + grid-template-columns: repeat(2, 1fr); + } + .admin-dashboard-tiles .tile { + padding: 0.75rem; + min-height: auto; + } + .admin-dashboard-tiles .tile h3 { + font-size: 1rem; + } + .admin-dashboard-tiles .tile p { + font-size: 0.875rem; + } + .admin-dashboard-tiles .tile .stat { + font-size: 1.25rem; + } } /* Role-based visibility classes */ diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css index 1a066d8..e7eb474 100644 --- a/app/assets/stylesheets/application.tailwind.css +++ b/app/assets/stylesheets/application.tailwind.css @@ -52,31 +52,11 @@ } h1, h2, h3, h4, h5, h6 { - @apply font-bold; + @apply font-bold text-black dark:text-gray-200; } -{{/* input[type=submit] { - @apply border bg-gray-400 rounded-md px-2 py-1; - &.primary { - @apply bg-teal-800 text-teal-50 border-teal-600; - } -} - -select, [type=text], [type=password] { - @apply pl-2 pr-6 py-1; +html.dark { + @apply text-gray-200; } -.dark { - input[type=submit] { - @apply bg-gray-600 text-slate-200; - } - select, [type=text], [type=password] { - @apply bg-zinc-900; - } - .session-holder { - select, [type=text], [type=password] { - @apply bg-white/60; - } - } -} */}} .session-holder { @apply w-full; diff --git a/app/views/admin/conferences/import_history.html.erb b/app/views/admin/conferences/import_history.html.erb index f8e0567..e5e4583 100644 --- a/app/views/admin/conferences/import_history.html.erb +++ b/app/views/admin/conferences/import_history.html.erb @@ -1,13 +1,13 @@ <div class="container mx-auto px-4 py-8"> - <div class="flex justify-between items-center mb-6"> + <div class="flex justify-between items-center mb-6 header-with-actions"> <h1 class="text-2xl font-bold dark:text-gray-200">Import History for <%= @conference.name %></h1> - <div class="flex space-x-2"> + <div class="flex space-x-2 actions"> <%= button_to "Run Import Now", retry_import_admin_conference_path(@conference), method: :post, class: "btn btn-primary" %> <%= link_to "Back to Conference", admin_conference_path(@conference), class: "btn btn-tertiary" %> </div> </div> - <div class="bg-white shadow overflow-hidden sm:rounded-lg dark:bg-gray-800"> + <div class="bg-white shadow overflow-hidden sm:rounded-lg dark:bg-gray-800 table-responsive"> <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> <thead class="bg-gray-50 dark:bg-gray-700"> <tr> diff --git a/app/views/admin/conferences/index.html.erb b/app/views/admin/conferences/index.html.erb index 1dfbf89..95e05a5 100644 --- a/app/views/admin/conferences/index.html.erb +++ b/app/views/admin/conferences/index.html.erb @@ -1,10 +1,12 @@ <div class="container mx-auto px-4 py-8"> - <div class="flex justify-between items-center mb-6"> - <h1 class="text-2xl font-bold dark:text-gray-200">Admin: Conferences</h1> - <%= link_to "New Conference", new_admin_conference_path, class: "btn btn-primary" %> + <div class="flex justify-between items-center mb-6 header-with-actions"> + <h1 class="text-2xl font-bold dark:text-gray-200">Conferences</h1> + <div class="actions"> + <%= link_to "New Conference", new_admin_conference_path, class: "btn btn-primary" %> + </div> </div> - <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg"> + <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg table-responsive"> <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> <thead class="bg-gray-50 dark:bg-gray-700"> <tr> diff --git a/app/views/admin/conferences/show.html.erb b/app/views/admin/conferences/show.html.erb index 285994e..bf4c6b1 100644 --- a/app/views/admin/conferences/show.html.erb +++ b/app/views/admin/conferences/show.html.erb @@ -1,7 +1,7 @@ <div class="container mx-auto px-4 py-8"> - <div class="flex justify-between items-center mb-6"> - <h1 class="text-2xl font-bold dark:text-gray-200">Conference: <%= @conference.name %></h1> - <div class="flex space-x-2"> + <div class="flex justify-between items-center mb-6 header-with-actions"> + <h1 class="text-2xl font-bold dark:text-gray-200"><%= @conference.name %></h1> + <div class="flex space-x-2 actions"> <%= link_to "Edit", edit_admin_conference_path(@conference), class: "btn btn-primary" %> <%= link_to "Back to List", admin_conferences_path, class: "btn btn-tertiary" %> </div> diff --git a/app/views/admin/dashboard/index.html.erb b/app/views/admin/dashboard/index.html.erb index 7027d9e..6319baa 100644 --- a/app/views/admin/dashboard/index.html.erb +++ b/app/views/admin/dashboard/index.html.erb @@ -2,45 +2,45 @@ <h1 class="text-2xl font-bold dark:text-gray-200 mb-6">Admin Dashboard</h1> <!-- Stats Overview --> - <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8"> - <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg"> - <div class="px-4 py-5 sm:p-6"> + <div class="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6 mb-8 admin-dashboard-tiles"> + <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg tile"> + <div class="px-3 py-3 sm:p-6"> <dl> <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Conferences</dt> - <dd class="mt-1 text-3xl font-semibold text-blue-600 dark:text-blue-400"> + <dd class="mt-1 text-2xl md:text-3xl font-semibold text-blue-600 dark:text-blue-400 stat"> <%= link_to @conferences_count, admin_conferences_path, class: "hover:underline" %> </dd> </dl> </div> </div> - <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg"> - <div class="px-4 py-5 sm:p-6"> + <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg tile"> + <div class="px-3 py-3 sm:p-6"> <dl> <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Sessions</dt> - <dd class="mt-1 text-3xl font-semibold text-blue-600 dark:text-blue-400"> + <dd class="mt-1 text-2xl md:text-3xl font-semibold text-blue-600 dark:text-blue-400 stat"> <%= @sessions_count %> </dd> </dl> </div> </div> - <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg"> - <div class="px-4 py-5 sm:p-6"> + <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg tile"> + <div class="px-3 py-3 sm:p-6"> <dl> <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Users</dt> - <dd class="mt-1 text-3xl font-semibold text-blue-600 dark:text-blue-400"> + <dd class="mt-1 text-2xl md:text-3xl font-semibold text-blue-600 dark:text-blue-400 stat"> <%= link_to @users_count, admin_users_path, class: "hover:underline" %> </dd> </dl> </div> </div> - <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg"> - <div class="px-4 py-5 sm:p-6"> + <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg tile"> + <div class="px-3 py-3 sm:p-6"> <dl> <dt class="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">Assignments</dt> - <dd class="mt-1 text-3xl font-semibold text-blue-600 dark:text-blue-400"> + <dd class="mt-1 text-2xl md:text-3xl font-semibold text-blue-600 dark:text-blue-400 stat"> <%= @assignments_count %> </dd> </dl> diff --git a/app/views/admin/roles/_form.html.erb b/app/views/admin/roles/_form.html.erb index e252231..d49afb3 100644 --- a/app/views/admin/roles/_form.html.erb +++ b/app/views/admin/roles/_form.html.erb @@ -37,11 +37,13 @@ <div class="px-4 py-5 sm:p-6"> <div class="space-y-2"> <% permissions.each do |permission| %> - <div class="flex items-center"> - <%= check_box_tag "role[permission_ids][]", permission.id, role.permissions.include?(permission), id: "permission_#{permission.id}", class: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" %> - <%= label_tag "permission_#{permission.id}", permission.name, class: "ml-2 block text-sm text-gray-900 dark:text-gray-300" %> + <div class="flex flex-col sm:flex-row sm:items-center mb-2"> + <div class="flex items-center"> + <%= check_box_tag "role[permission_ids][]", permission.id, role.permissions.include?(permission), id: "permission_#{permission.id}", class: "h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" %> + <%= label_tag "permission_#{permission.id}", permission.name, class: "ml-2 text-sm text-gray-900 dark:text-gray-300" %> + </div> <% if permission.description.present? %> - <span class="ml-2 text-xs text-gray-500 dark:text-gray-400"><%= permission.description %></span> + <span class="mt-1 sm:mt-0 sm:ml-2 block text-xs text-gray-500 dark:text-gray-400"><%= permission.description %></span> <% end %> </div> <% end %> diff --git a/app/views/admin/roles/edit.html.erb b/app/views/admin/roles/edit.html.erb index ce796b2..4476fd9 100644 --- a/app/views/admin/roles/edit.html.erb +++ b/app/views/admin/roles/edit.html.erb @@ -1,5 +1,5 @@ <div class="container mx-auto px-4 py-8"> - <h1 class="text-2xl font-bold mb-6 dark:text-gray-200">Admin: Edit Role: <%= @role.name %></h1> + <h1 class="text-2xl font-bold mb-6 dark:text-gray-200">Edit Role: <%= @role.name %></h1> <%= render "form", role: @role, permissions: @permissions, submit_text: "Update Role" %> </div> diff --git a/app/views/admin/roles/index.html.erb b/app/views/admin/roles/index.html.erb index ccb0665..8499910 100644 --- a/app/views/admin/roles/index.html.erb +++ b/app/views/admin/roles/index.html.erb @@ -1,7 +1,7 @@ <div class="container mx-auto px-4 py-8"> - <h1 class="text-2xl font-bold mb-6 dark:text-gray-200">Admin: Roles and Permissions</h1> + <h1 class="text-2xl font-bold mb-6 dark:text-gray-200">Roles and Permissions</h1> - <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg"> + <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg table-responsive"> <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> <thead class="bg-gray-50 dark:bg-gray-700"> <tr> diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb index 0168ea3..3d96881 100644 --- a/app/views/admin/users/index.html.erb +++ b/app/views/admin/users/index.html.erb @@ -1,10 +1,12 @@ <div class="container mx-auto px-4 py-8"> - <div class="flex justify-between items-center mb-6"> - <h1 class="text-2xl font-bold dark:text-gray-200">Admin: Users</h1> - <%= link_to "New User", new_admin_user_path, class: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800" %> + <div class="flex justify-between items-center mb-6 header-with-actions"> + <h1 class="text-2xl font-bold dark:text-gray-200">Users</h1> + <div class="actions"> + <%= link_to "New User", new_admin_user_path, class: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800" %> + </div> </div> - <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg"> + <div class="bg-white dark:bg-gray-800 shadow overflow-hidden rounded-lg table-responsive"> <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> <thead class="bg-gray-50 dark:bg-gray-700"> <tr> diff --git a/app/views/conferences/show.html.erb b/app/views/conferences/show.html.erb index 1afa46f..0b5d0fc 100644 --- a/app/views/conferences/show.html.erb +++ b/app/views/conferences/show.html.erb @@ -58,7 +58,7 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone) timeline_ends_at = day_ends_at.advance(minutes: (day_ends_at.min / timeline_granularity.to_f).ceil * timeline_granularity) %> <div class="conference-day my-8" id="<%= date.strftime('day-%Y-%m-%d') %>"> - <h3 class="text-xl my-4 border-b sticky top-0 md:top-[3.7rem] bg-white dark:bg-gray-900 bg-opacity-70 dark:bg-opacity-70 z-30"><%= date.strftime('%B %d, %Y') %></h3> + <h3 class="text-xl my-4 border-b sticky top-0 md:top-[3.7rem] bg-white dark:bg-gray-900 bg-opacity-70 dark:bg-opacity-70 z-40"><%= date.strftime('%B %d, %Y') %></h3> <% =begin%> <ul class="list-disc"> @@ -69,7 +69,7 @@ current_time = Time.zone.now.in_time_zone(@conference.time_zone) </ul> <% =end %> - <div class="day-wrapper flex relative"> + <div class="day-wrapper flex relative table-responsive overflow-x-auto -webkit-overflow-scrolling-touch"> <div class="times sticky left-0 bg-white dark:bg-gray-900 bg-opacity-70 dark:bg-opacity-70 z-20"> <h4>Time</h4> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index c6c4711..b8fd29f 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -34,24 +34,27 @@ <body <%= tag.attributes(body_data_attributes) %> class="bg-gray-100 dark:bg-gray-900 min-h-screen"> <header class="bg-white dark:bg-gray-800 shadow"> <div class="container mx-auto px-4 py-4 flex justify-between items-center"> - <div class="flex items-center space-x-4"> + <div class="flex items-center"> <h1 class="text-xl font-bold text-gray-900 dark:text-white"> - <%= link_to "re:scheduled", root_path %> + <%= link_to "re:scheduled", root_path, class: "whitespace-nowrap" %> </h1> - <%= link_to 'Conferences', conferences_path, class: 'text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white' %> - <%= link_to 'Assignments', assignments_path, class: 'text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white' %> + <!-- Desktop navigation items --> + <div class="hidden md:flex md:items-center md:space-x-4 md:ml-4"> + <%= link_to 'Conferences', conferences_path, class: 'text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white' %> + <%= link_to 'Assignments', assignments_path, class: 'text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white' %> + </div> </div> - <nav class="flex items-center space-x-4"> + <nav class="flex items-center"> <% if user_signed_in? %> - <div class="flex items-center space-x-4 ml-4"> - <%= render partial: "application/user_avatar", locals: { user: current_user } %> + <!-- Desktop user navigation --> + <div class="hidden md:flex md:items-center md:space-x-4"> <%= link_to edit_user_registration_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white", aria_label: "My Profile" do %> - <span class="hidden lg:inline">My </span>Profile + <span>My Profile</span> <% end %> <%= link_to user_assignments_path(current_user), class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" do %> - <span class="hidden lg:inline">My </span>Assignments + <span>My Assignments</span> <% end %> <%= link_to "Logout", destroy_user_session_path, data: { turbo_method: :delete }, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %> @@ -59,9 +62,47 @@ <%= link_to "Admin", admin_root_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %> <% end %> </div> + + <!-- User avatar always visible --> + <div class="flex items-center ml-4"> + <%= render partial: "application/user_avatar", locals: { user: current_user } %> + </div> + + <!-- Mobile hamburger menu --> + <div class="md:hidden relative ml-4"> + <button id="main-menu-toggle" class="text-gray-900 dark:text-white"> + <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> + </svg> + </button> + + <!-- Mobile dropdown menu --> + <div id="main-mobile-menu" class="hidden absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded shadow-lg z-50"> + <div class="py-2"> + <div class="px-4 py-2 font-semibold text-sm text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700">Navigation</div> + <%= link_to 'Conferences', conferences_path, class: 'block px-4 py-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700' %> + <%= link_to 'Assignments', assignments_path, class: 'block px-4 py-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700' %> + + <div class="px-4 py-2 font-semibold text-sm text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700 mt-2">Account</div> + <%= link_to edit_user_registration_path, class: "block px-4 py-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700" do %> + My Profile + <% end %> + <%= link_to user_assignments_path(current_user), class: "block px-4 py-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700" do %> + My Assignments + <% end %> + + <% if current_user.has_role?("admin") || current_user.has_role?("events_admin") %> + <%= link_to "Admin", admin_root_path, class: "block px-4 py-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700" %> + <% end %> + + <%= link_to "Logout", destroy_user_session_path, data: { turbo_method: :delete }, class: "block px-4 py-2 text-gray-600 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-700" %> + </div> + </div> + </div> <% else %> + <!-- Not logged in state --> <div class="flex items-center space-x-4"> - <span class="px-2 text-gray-600 dark:text-gray-300">not logged in</span> + <span class="px-2 text-gray-600 dark:text-gray-300 hidden md:inline">not logged in</span> <%= link_to "Log in", new_user_session_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %> <%= link_to "Sign Up", new_user_registration_path, class: "text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white" %> </div> @@ -70,6 +111,38 @@ </div> </header> + <script> + function initializeMainNavMenu() { + const menuToggle = document.getElementById('main-menu-toggle'); + const mobileMenu = document.getElementById('main-mobile-menu'); + + if (menuToggle && mobileMenu) { + // Remove any existing event listeners to prevent duplicates + const newMenuToggle = menuToggle.cloneNode(true); + menuToggle.parentNode.replaceChild(newMenuToggle, menuToggle); + + newMenuToggle.addEventListener('click', function(e) { + e.stopPropagation(); + mobileMenu.classList.toggle('hidden'); + }); + + // Close the menu when clicking outside + document.addEventListener('click', function(e) { + if (mobileMenu && !mobileMenu.contains(e.target) && e.target !== newMenuToggle) { + mobileMenu.classList.add('hidden'); + } + }); + } + } + + // Initialize on DOMContentLoaded + document.addEventListener('DOMContentLoaded', initializeMainNavMenu); + + // Re-initialize on Turbo navigation + document.addEventListener('turbo:load', initializeMainNavMenu); + document.addEventListener('turbo:render', initializeMainNavMenu); + </script> + <main> <% if notice %> <div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4 mx-4 mt-4 diff --git a/app/views/shared/_admin_nav.html.erb b/app/views/shared/_admin_nav.html.erb index a43915e..cc78a3e 100644 --- a/app/views/shared/_admin_nav.html.erb +++ b/app/views/shared/_admin_nav.html.erb @@ -1,19 +1,94 @@ <header class="bg-gray-800 dark:bg-stone-200 text-white dark:text-black shadow"> <div class="container mx-auto px-4 py-4 flex justify-between items-center"> - <div class="flex items-center space-x-4"> - <h1 class="text-xl font-bold"> - <%= link_to "re:scheduled admin", admin_root_path %> + <div class="flex items-center"> + <h1 class="text-xl font-bold text-white dark:text-black"> + <%= link_to "re:scheduled admin", admin_root_path, class: "whitespace-nowrap hidden md:inline" %> + <%= link_to "re:admin", admin_root_path, class: "whitespace-nowrap md:hidden" %> </h1> - <%= link_to "Dashboard", admin_dashboard_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> - <%= link_to "Users", admin_users_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> - <%= link_to "Roles", admin_roles_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> - <%= link_to "Conferences", admin_conferences_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> + <div class="hidden md:flex md:items-center md:space-x-4 md:ml-4"> + <%= link_to "Dashboard", admin_dashboard_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> + <%= link_to "Users", admin_users_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> + <%= link_to "Roles", admin_roles_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> + <%= link_to "Conferences", admin_conferences_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> + </div> + + <!-- Mobile visible key nav links --> + <div class="ml-4 md:hidden flex overflow-hidden whitespace-nowrap"> + <%= link_to "Users", admin_users_path, class: "text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black mr-3" %> + </div> </div> - <nav class="flex items-center space-x-4"> - <%= render partial: "application/user_avatar", locals: { user: current_user } %> - <%= link_to "Back to Site", root_path, class: "ml-4 text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" %> + <nav class="flex items-center space-x-3"> + <!-- User avatar --> + <div class="flex items-center"> + <%= render partial: "application/user_avatar", locals: { user: current_user } %> + </div> + + <!-- Back to site link with curved arrow icon --> + <%= link_to root_path, class: "flex items-center justify-center text-gray-300 dark:text-gray-600 hover:text-white dark:hover:text-black" do %> + <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"> + <path d="M9 10.5l-5 3.5 5 3.5v-2.5h8a4 4 0 0 0 0-8h-3v2h3a2 2 0 1 1 0 4H9v-2.5z"/> + </svg> + <% end %> + + <!-- Hamburger menu button - mobile only --> + <div class="relative md:hidden"> + <button id="admin-menu-toggle" class="text-white dark:text-black"> + <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> + </svg> + </button> + + <!-- Dropdown menu instead of overlay --> + <div id="admin-mobile-menu" class="hidden absolute right-0 mt-2 w-48 bg-gray-800 dark:bg-stone-100 rounded shadow-lg z-50"> + <div class="py-2"> + <%= link_to "Dashboard", admin_dashboard_path, class: "block px-4 py-2 text-gray-300 dark:text-gray-600 hover:bg-gray-700 dark:hover:bg-stone-200" %> + <%= link_to "Users", admin_users_path, class: "block px-4 py-2 text-gray-300 dark:text-gray-600 hover:bg-gray-700 dark:hover:bg-stone-200" %> + <%= link_to "Roles", admin_roles_path, class: "block px-4 py-2 text-gray-300 dark:text-gray-600 hover:bg-gray-700 dark:hover:bg-stone-200" %> + <%= link_to "Conferences", admin_conferences_path, class: "block px-4 py-2 text-gray-300 dark:text-gray-600 hover:bg-gray-700 dark:hover:bg-stone-200" %> + <div class="border-t border-gray-700 dark:border-gray-300 my-1"></div> + <%= link_to root_path, class: "block px-4 py-2 text-gray-300 dark:text-gray-600 hover:bg-gray-700 dark:hover:bg-stone-200 flex items-center" do %> + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 mr-2"> + <path d="M9 10.5l-5 3.5 5 3.5v-2.5h8a4 4 0 0 0 0-8h-3v2h3a2 2 0 1 1 0 4H9v-2.5z"/> + </svg> + Back to Site + <% end %> + </div> + </div> + </div> </nav> </div> + + <script> + function initializeAdminNavMenu() { + const menuToggle = document.getElementById('admin-menu-toggle'); + const mobileMenu = document.getElementById('admin-mobile-menu'); + + if (menuToggle && mobileMenu) { + // Remove any existing event listeners to prevent duplicates + const newMenuToggle = menuToggle.cloneNode(true); + menuToggle.parentNode.replaceChild(newMenuToggle, menuToggle); + + newMenuToggle.addEventListener('click', function(e) { + e.stopPropagation(); + mobileMenu.classList.toggle('hidden'); + }); + + // Close the menu when clicking outside + document.addEventListener('click', function(e) { + if (mobileMenu && !mobileMenu.contains(e.target) && e.target !== newMenuToggle) { + mobileMenu.classList.add('hidden'); + } + }); + } + } + + // Initialize on DOMContentLoaded + document.addEventListener('DOMContentLoaded', initializeAdminNavMenu); + + // Re-initialize on Turbo navigation + document.addEventListener('turbo:load', initializeAdminNavMenu); + document.addEventListener('turbo:render', initializeAdminNavMenu); + </script> </header> -- GitLab