Compare commits
3 Commits
907e51fc60
...
055640b73e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
055640b73e | ||
|
|
a7e83d79d7 | ||
|
|
9404f10c93 |
73
app/javascript/controllers/header_controller.js
Normal file
73
app/javascript/controllers/header_controller.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
// Controller for handling the header navigation
|
||||||
|
// Manages mobile menu toggle and user dropdown menu
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["mobileMenu", "mobileMenuButton", "userMenu", "userMenuButton"]
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
// Initialize menu states
|
||||||
|
this.mobileMenuOpen = false
|
||||||
|
this.userMenuOpen = false
|
||||||
|
|
||||||
|
// Add click outside listener for user menu
|
||||||
|
this.clickOutsideHandler = this.handleClickOutside.bind(this)
|
||||||
|
document.addEventListener("click", this.clickOutsideHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
// Clean up event listener
|
||||||
|
document.removeEventListener("click", this.clickOutsideHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle mobile menu visibility
|
||||||
|
toggleMobileMenu() {
|
||||||
|
this.mobileMenuOpen = !this.mobileMenuOpen
|
||||||
|
this.mobileMenuTarget.classList.toggle("hidden", !this.mobileMenuOpen)
|
||||||
|
|
||||||
|
// Update button icon based on state
|
||||||
|
const iconOpen = this.mobileMenuButtonTarget.querySelector('[data-menu-icon="open"]')
|
||||||
|
const iconClose = this.mobileMenuButtonTarget.querySelector('[data-menu-icon="close"]')
|
||||||
|
|
||||||
|
if (iconOpen && iconClose) {
|
||||||
|
iconOpen.classList.toggle("hidden", this.mobileMenuOpen)
|
||||||
|
iconClose.classList.toggle("hidden", !this.mobileMenuOpen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle user dropdown menu visibility
|
||||||
|
toggleUserMenu() {
|
||||||
|
this.userMenuOpen = !this.userMenuOpen
|
||||||
|
if (this.hasUserMenuTarget) {
|
||||||
|
this.userMenuTarget.classList.toggle("hidden", !this.userMenuOpen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close menus when clicking outside
|
||||||
|
handleClickOutside(event) {
|
||||||
|
// Close user menu if clicked outside
|
||||||
|
if (this.userMenuOpen && this.hasUserMenuTarget &&
|
||||||
|
!this.userMenuTarget.contains(event.target) &&
|
||||||
|
!this.userMenuButtonTarget.contains(event.target)) {
|
||||||
|
this.userMenuOpen = false
|
||||||
|
this.userMenuTarget.classList.add("hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mobile menu if clicked outside
|
||||||
|
if (this.mobileMenuOpen &&
|
||||||
|
!this.mobileMenuTarget.contains(event.target) &&
|
||||||
|
!this.mobileMenuButtonTarget.contains(event.target)) {
|
||||||
|
this.mobileMenuOpen = false
|
||||||
|
this.mobileMenuTarget.classList.add("hidden")
|
||||||
|
|
||||||
|
// Update button icon
|
||||||
|
const iconOpen = this.mobileMenuButtonTarget.querySelector('[data-menu-icon="open"]')
|
||||||
|
const iconClose = this.mobileMenuButtonTarget.querySelector('[data-menu-icon="close"]')
|
||||||
|
|
||||||
|
if (iconOpen && iconClose) {
|
||||||
|
iconOpen.classList.remove("hidden")
|
||||||
|
iconClose.classList.add("hidden")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,10 +2,8 @@
|
|||||||
// Run that command whenever you add a new controller or create them with
|
// Run that command whenever you add a new controller or create them with
|
||||||
// ./bin/rails generate stimulus controllerName
|
// ./bin/rails generate stimulus controllerName
|
||||||
|
|
||||||
// Import the main Stimulus application
|
|
||||||
import { application } from "./application"
|
import { application } from "./application"
|
||||||
|
|
||||||
// Import all controllers
|
|
||||||
import LogoutController from "./logout_controller";
|
import LogoutController from "./logout_controller";
|
||||||
application.register("logout", LogoutController);
|
application.register("logout", LogoutController);
|
||||||
|
|
||||||
@@ -18,6 +16,9 @@ application.register("flash-message", FlashMessageController);
|
|||||||
import TicketSelectionController from "./ticket_selection_controller"
|
import TicketSelectionController from "./ticket_selection_controller"
|
||||||
application.register("ticket-selection", TicketSelectionController);
|
application.register("ticket-selection", TicketSelectionController);
|
||||||
|
|
||||||
|
import HeaderController from "./header_controller"
|
||||||
|
application.register("header", HeaderController);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
<header class="bg-neutral-800 border-b border-neutral-700">
|
<header class="bg-neutral-800 border-b border-neutral-700">
|
||||||
<nav x-data="{ open: false }" class="container mx-auto px-4 sm:px-6 lg:px-8">
|
<nav data-controller="header" class="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex justify-between h-16">
|
<div class="flex justify-between h-16">
|
||||||
<!-- Logo & Navigation -->
|
<!-- Logo -->
|
||||||
<div class="flex items-center space-x-8">
|
<div class="flex items-center">
|
||||||
<%= link_to Rails.application.config.app_name, current_user ? "/dashboard" : "/",
|
<%= link_to Rails.application.config.app_name, current_user ? "/dashboard" : "/",
|
||||||
class: "text-xl font-bold text-white" %>
|
class: "text-xl font-bold text-white" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="hidden sm:flex space-x-6">
|
<!-- Desktop Navigation -->
|
||||||
|
<div class="hidden sm:flex items-center space-x-6">
|
||||||
<%= link_to t("header.parties"), events_path,
|
<%= link_to t("header.parties"), events_path,
|
||||||
class: "text-gray-100 hover:text-purple-200 py-2 rounded-md text-sm font-medium transition-colors duration-200" %>
|
class: "text-gray-100 hover:text-purple-200 py-2 rounded-md text-sm font-medium transition-colors duration-200" %>
|
||||||
<%= link_to t("header.concerts"), "#",
|
<%= link_to t("header.concerts"), "#",
|
||||||
class: "text-gray-100 hover:text-purple-200 py-2 rounded-md text-sm font-medium transition-colors duration-200" %>
|
class: "text-gray-100 hover:text-purple-200 py-2 rounded-md text-sm font-medium transition-colors duration-200" %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Authentication -->
|
<!-- Authentication -->
|
||||||
<div class="flex items-center space-x-4">
|
<div class="hidden sm:flex items-center space-x-4">
|
||||||
<% if user_signed_in? %>
|
<% if user_signed_in? %>
|
||||||
<div class="relative" x-data="{ open: false }" @click.outside="open = false">
|
<div class="relative" data-header-target="userMenuButton">
|
||||||
<button @click="open = !open"
|
<button data-action="click->header#toggleUserMenu"
|
||||||
class="bg-purple-700 text-white border border-purple-800 font-medium py-2 px-4 rounded-lg hover:bg-purple-800 transition-colors duration-200 flex items-center space-x-2">
|
class="bg-purple-700 text-white border border-purple-800 font-medium py-2 px-4 rounded-lg hover:bg-purple-800 transition-colors duration-200 flex items-center space-x-2">
|
||||||
<span><%= current_user.email.length > 20 ? current_user.email[0,20] + "..." : current_user.email %></span>
|
<span><%= current_user.email.length > 20 ? current_user.email[0,20] + "..." : current_user.email %></span>
|
||||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||||
@@ -26,8 +27,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div x-show="open" x-transition
|
<div data-header-target="userMenu" class="absolute right-0 mt-2 w-48 rounded-md shadow-lg z-50 hidden">
|
||||||
class="absolute right-0 mt-2 w-48 rounded-md shadow-lg z-50">
|
|
||||||
<%= link_to t("header.profile"), edit_user_registration_path,
|
<%= link_to t("header.profile"), edit_user_registration_path,
|
||||||
class: "block px-4 py-2 text-sm bg-black text-gray-100 hover:bg-purple-700 first:rounded-t-md" %>
|
class: "block px-4 py-2 text-sm bg-black text-gray-100 hover:bg-purple-700 first:rounded-t-md" %>
|
||||||
<%= link_to t("header.reservations"), "#",
|
<%= link_to t("header.reservations"), "#",
|
||||||
@@ -39,25 +39,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="hidden sm:flex items-center space-x-4">
|
|
||||||
<%= link_to t("header.login"), new_user_session_path,
|
<%= link_to t("header.login"), new_user_session_path,
|
||||||
class: "bg-black text-gray-100 hover:text-purple-200 px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200" %>
|
class: "bg-black text-gray-100 hover:text-purple-200 px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200" %>
|
||||||
<%= link_to t("header.register"), new_user_registration_path,
|
<%= link_to t("header.register"), new_user_registration_path,
|
||||||
class: "bg-purple-600 text-white font-medium py-2 px-4 rounded-lg hover:bg-purple-700 transition-colors duration-200" %>
|
class: "bg-purple-600 text-white font-medium py-2 px-4 rounded-lg hover:bg-purple-700 transition-colors duration-200" %>
|
||||||
</div>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
<!-- Mobile Menu Button -->
|
</div>
|
||||||
<button @click="open = !open" class="sm:hidden p-2 rounded-md text-neutral-300 hover:text-white hover:bg-purple-700">
|
|
||||||
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
<!-- Mobile menu button -->
|
||||||
<path :class="{ "hidden": open }" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
<div class="flex items-center sm:hidden">
|
||||||
<path :class="{ "hidden": !open }" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
<button data-action="click->header#toggleMobileMenu" data-header-target="mobileMenuButton" class="p-2 rounded-md text-neutral-300 hover:text-white hover:bg-purple-700">
|
||||||
|
<svg data-menu-icon="open" class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
</svg>
|
||||||
|
<svg data-menu-icon="close" class="h-6 w-6 hidden" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile Menu -->
|
<!-- Mobile Menu -->
|
||||||
<div :class="{ "block": open, "hidden": !open }" class="hidden sm:hidden">
|
<div data-header-target="mobileMenu" class="hidden sm:hidden">
|
||||||
<div class="px-2 pt-2 pb-3 space-y-1">
|
<div class="px-2 pt-2 pb-3 space-y-1">
|
||||||
<%= link_to t("header.parties"), events_path,
|
<%= link_to t("header.parties"), events_path,
|
||||||
class: "block px-3 py-2 rounded-md text-base font-medium bg-black text-gray-100 hover:text-purple-200 hover:bg-purple-700" %>
|
class: "block px-3 py-2 rounded-md text-base font-medium bg-black text-gray-100 hover:text-purple-200 hover:bg-purple-700" %>
|
||||||
|
|||||||
145
app/views/components/_header.html.erb.old
Normal file
145
app/views/components/_header.html.erb.old
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<!--
|
||||||
|
Note: The logout method is handled by logout_controller
|
||||||
|
in app/javascript/controllers/logout_controller.js
|
||||||
|
-->
|
||||||
|
<nav x-data="{ open: false }" class="bg-black border-b border-gray-100">
|
||||||
|
<!-- Primary Navigation Menu -->
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="flex justify-between h-16">
|
||||||
|
<div class="flex">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="shrink-0 flex items-center">
|
||||||
|
<%= link_to "Cyanet", "test", class: "text-white" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Navigation Links -->
|
||||||
|
<div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
|
||||||
|
<!-- Dashboard -->
|
||||||
|
<%= link_to "Espace client", "#",
|
||||||
|
class: "inline-flex
|
||||||
|
items-center px-1 pt-1 border-b-2 border-transparent
|
||||||
|
text-sm font-medium leading-5 text-gray-300
|
||||||
|
hover:text-gray-500 hover:border-gray-300 focus:outline-none
|
||||||
|
focus:text-gray-700 focus:border-gray-300 transition
|
||||||
|
duration-150 ease-in-out" %>
|
||||||
|
<!-- ./Dashboard -->
|
||||||
|
|
||||||
|
<!-- My services -->
|
||||||
|
<%= link_to "Mes services", "#",
|
||||||
|
class: "inline-flex
|
||||||
|
items-center px-1 pt-1 border-b-2 border-transparent
|
||||||
|
text-sm font-medium leading-5 text-gray-300
|
||||||
|
hover:text-gray-500 hover:border-gray-300 focus:outline-none
|
||||||
|
focus:text-gray-700 focus:border-gray-300 transition
|
||||||
|
duration-150 ease-in-out" %>
|
||||||
|
<!-- ./My services-->
|
||||||
|
|
||||||
|
<!-- My services -->
|
||||||
|
<%= link_to "Mes factures", "#",
|
||||||
|
class: "inline-flex
|
||||||
|
items-center px-1 pt-1 border-b-2 border-transparent
|
||||||
|
text-sm font-medium leading-5 text-gray-300
|
||||||
|
hover:text-gray-500 hover:border-gray-300 focus:outline-none
|
||||||
|
focus:text-gray-700 focus:border-gray-300 transition
|
||||||
|
duration-150 ease-in-out" %>
|
||||||
|
<!-- ./My services-->
|
||||||
|
|
||||||
|
<!-- Support -->
|
||||||
|
<%= link_to "Support", "#",
|
||||||
|
class: "inline-flex
|
||||||
|
items-center px-1 pt-1 border-b-2 border-transparent
|
||||||
|
text-sm font-medium leading-5 text-gray-300
|
||||||
|
hover:text-gray-500 hover:border-gray-300 focus:outline-none
|
||||||
|
focus:text-gray-700 focus:border-gray-300 transition
|
||||||
|
duration-150 ease-in-out" %>
|
||||||
|
<!-- ./Support-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Settings Dropdown -->
|
||||||
|
<div class="hidden sm:flex sm:items-center sm:ms-6">
|
||||||
|
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
|
||||||
|
<div @click="open = ! open">
|
||||||
|
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150">
|
||||||
|
<div>Mon profil</div>
|
||||||
|
<div class="ms-1">
|
||||||
|
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div x-show="open"
|
||||||
|
x-transition:enter="transition ease-out duration-200"
|
||||||
|
x-transition:enter-start="opacity-0 scale-95"
|
||||||
|
x-transition:enter-end="opacity-100 scale-100"
|
||||||
|
x-transition:leave="transition ease-in duration-75"
|
||||||
|
x-transition:leave-start="opacity-100 scale-100"
|
||||||
|
x-transition:leave-end="opacity-0 scale-95"
|
||||||
|
class="absolute z-50 mt-2 w-48 rounded-md shadow-lg origin-top-right right-0"
|
||||||
|
style="display: none;"
|
||||||
|
@click="open = false">
|
||||||
|
<div class="rounded-md ring-1 ring-black ring-opacity-5 py-1 bg-white">
|
||||||
|
<%= link_to "Profil", "#", class: "block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out" %>
|
||||||
|
|
||||||
|
<!-- Logout -->
|
||||||
|
<%= link_to "Déconnexion", destroy_user_session_path,
|
||||||
|
data: {
|
||||||
|
controller: "logout",
|
||||||
|
action: "click->logout#signOut",
|
||||||
|
logout_url_value: destroy_user_session_path,
|
||||||
|
login_url_value: new_user_session_path,
|
||||||
|
turbo: false
|
||||||
|
},
|
||||||
|
class: "inline-block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out" %>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Hamburger -->
|
||||||
|
<div class="-me-2 flex items-center sm:hidden">
|
||||||
|
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out">
|
||||||
|
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path :class="{ 'hidden': open, 'inline-flex': !open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
<path :class="{ 'hidden': !open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Responsive Navigation Menu -->
|
||||||
|
<div :class="{ 'block': open, 'hidden': !open }" class="hidden sm:hidden">
|
||||||
|
<div class="pt-2 pb-3 space-y-1">
|
||||||
|
<%= link_to "Dashboard", "test", class: "block pl-3 pr-4 py-2 border-l-4 border-transparent text-base font-medium text-gray-400 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Responsive Settings Options -->
|
||||||
|
<div class="pt-4 pb-1 border-t border-gray-200">
|
||||||
|
<div class="px-4">
|
||||||
|
<div class="font-medium text-base text-gray-800">Test</div>
|
||||||
|
<div class="font-medium text-sm text-gray-500">Test</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3 space-y-1">
|
||||||
|
<%= link_to "Profile", "test", class: "block w-full pl-3 pr-4 py-2 border-l-4 border-transparent text-base font-medium text-gray-400 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out" %>
|
||||||
|
|
||||||
|
<!-- Logout -->
|
||||||
|
<%= link_to "Déconnexion", destroy_user_session_path,
|
||||||
|
data: {
|
||||||
|
controller: "logout",
|
||||||
|
action: "click->logout#signOut",
|
||||||
|
logout_url_value: destroy_user_session_path,
|
||||||
|
login_url_value: new_user_session_path,
|
||||||
|
turbo: false
|
||||||
|
},
|
||||||
|
class: "block w-full pl-3 pr-4 py-2 border-l-4 border-transparent text-base font-medium text-gray-400 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out" %>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
Reference in New Issue
Block a user