working profile pic, but its trash, redoing the navbar icons now

This commit is contained in:
Priec
2026-06-19 22:34:11 +02:00
parent 454d5cb349
commit 673b28c361
4 changed files with 71 additions and 45 deletions

View File

@@ -57,6 +57,9 @@
</script>
<link href="/static/css/app.css?v=2026-06-16" rel="stylesheet" type="text/css">
<script src="/static/vendor/htmx/htmx-1.9.12.min.js"></script>
<!-- Alpine Focus plugin (x-trap / $focus) — must load before Alpine core;
required by the Penguin UI keyboard-accessible dropdowns. -->
<script defer src="/static/vendor/alpine/alpine-focus-3.14.9.min.js"></script>
<script defer src="/static/vendor/alpine/alpinejs-3.14.9.min.js"></script>
</head>
<body hx-boost="true"
@@ -95,8 +98,13 @@
<!-- right side: cart + settings + mobile toggle -->
<div class="ml-auto flex items-center gap-1">
<!-- cart with live item-count badge read from the `cart` cookie -->
<a href="/cart" data-nav="/cart"
<!-- customer profile dropdown (avatar + name + account type) -->
{% if logged_in_customer %}
{% include "partials/profile_menu.html" %}
{% endif %}
<!-- cart with live item-count badge read from the `cart` cookie.
hx-boost=false: a plain full-page navigation to /cart, no SPA swap. -->
<a href="/cart" data-nav="/cart" hx-boost="false"
x-data="{ count: 0 }"
x-init="count = cartCount(); ['htmx:afterSwap', 'htmx:afterRequest'].forEach(function (e) { window.addEventListener(e, function () { count = cartCount() }) })"
aria-label="{{ t(key='cart-title', lang=lang | default(value='sk')) }}"
@@ -106,12 +114,6 @@
<span x-show="count > 0" x-cloak x-text="count"
class="absolute -right-1 -top-1 inline-flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-semibold leading-4 text-on-primary dark:bg-primary-dark dark:text-on-primary-dark"></span>
</a>
<!-- customer profile dropdown (avatar + name + account type) -->
{% if logged_in_customer %}
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative">
{% include "partials/profile_menu.html" %}
</div>
{% endif %}
<!-- settings (language + theme) dropdown -->
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative">

View File

@@ -1,46 +1,55 @@
{# Customer profile dropdown shown in the storefront navbar next to the cart.
Trigger shows an initials avatar, the customer's name and their account type
(personal / company); clicking opens a quick-navigation menu.
{# Customer profile dropdown in the storefront navbar.
The host template provides the wrapper
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative">. #}
Penguin UI markup (navbar/with-user-profile.html dropdown + avatar rendered as
avatar-with-initials.html, primary variant, since we have no photo), but with
the same minimal core-Alpine toggle as settings_dropdown.html — plain
open / @click.outside, NOT Penguin's x-trap / $focus keyboard nav (that needs
the Alpine Focus plugin we don't rely on). Self-contained Alpine state.
{# initials from the name, e.g. "Filip Priec" -> "FP" #}
IMPORTANT: the dropdown <ul> needs an inline style="display: none" in addition
to x-cloak. Because it has id="userMenu", htmx hx-boost "settles" it by id
across boosted navigations, and that interferes with Alpine re-applying x-show,
leaving the menu visible (open=false) after navigating. The inline display:none
keeps it hidden until x-show explicitly opens it. (settings_dropdown has no id,
so it is not settled and does not need this.) #}
{# initials from the full name, e.g. "Filip Priec" -> "FP" #}
{% set _name = customer_name | default(value='') | trim %}
{% set _parts = _name | split(pat=' ') %}
{% set _initials = _parts.0 | truncate(length=1, end='') | upper %}
{% if _parts | length > 1 %}{% set _second = _parts | last | truncate(length=1, end='') | upper %}{% set _initials = _initials ~ _second %}{% endif %}
{% if customer_account_type == "company" %}{% set _type_label = t(key="account-company", lang=lang | default(value='sk')) %}{% else %}{% set _type_label = t(key="account-personal", lang=lang | default(value='sk')) %}{% endif %}
<button type="button" @click="open = !open" :aria-expanded="open"
aria-label="{{ t(key='nav-account', lang=lang | default(value='sk')) }}"
class="flex items-center gap-2 rounded-radius border border-outline bg-surface-alt py-1 pl-1 pr-2 text-left transition hover:border-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt dark:hover:border-primary-dark">
<span class="inline-flex size-7 shrink-0 items-center justify-center rounded-full bg-primary text-xs font-semibold text-on-primary dark:bg-primary-dark dark:text-on-primary-dark">{{ _initials }}</span>
<span class="hidden min-w-0 flex-col leading-tight sm:flex">
<span class="truncate text-xs font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ _name }}</span>
<span class="truncate text-[10px] text-on-surface/60 dark:text-on-surface-dark/60">{{ _type_label }}</span>
</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="hidden size-4 text-on-surface/60 sm:block dark:text-on-surface-dark/60" :class="open && 'rotate-180'"><path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
</button>
{% set _person_icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" fill="currentColor" class="size-5"><path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z" clip-rule="evenodd"/></svg>' %}
<div x-show="open" x-cloak @click.outside="open = false" x-transition.origin.top.right
class="absolute right-0 mt-2 flex w-56 flex-col overflow-hidden rounded-radius border border-outline bg-surface-alt py-1 shadow-lg dark:border-outline-dark dark:bg-surface-dark-alt"
role="menu">
<div class="flex items-center gap-3 border-b border-outline px-4 py-3 dark:border-outline-dark">
<span class="inline-flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-sm font-semibold text-on-primary dark:bg-primary-dark dark:text-on-primary-dark">{{ _initials }}</span>
<span class="flex min-w-0 flex-col leading-tight">
<span class="truncate text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ _name }}</span>
<span class="truncate text-xs text-on-surface/60 dark:text-on-surface-dark/60">{{ _type_label }}</span>
</span>
</div>
<a href="/account/orders" data-nav="/account/orders" role="menuitem"
class="px-4 py-2 text-sm text-on-surface transition hover:bg-primary/5 hover:text-on-surface-strong focus-visible:bg-primary/10 focus-visible:outline-hidden dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong">{{ t(key="account-orders", lang=lang | default(value='sk')) }}</a>
<a href="/account/profile" data-nav="/account/profile" role="menuitem"
class="px-4 py-2 text-sm text-on-surface transition hover:bg-primary/5 hover:text-on-surface-strong focus-visible:bg-primary/10 focus-visible:outline-hidden dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong">{{ t(key="profile-title", lang=lang | default(value='sk')) }}</a>
<a href="/account/password" data-nav="/account/password" role="menuitem"
class="px-4 py-2 text-sm text-on-surface transition hover:bg-primary/5 hover:text-on-surface-strong focus-visible:bg-primary/10 focus-visible:outline-hidden dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong">{{ t(key="account-change-password", lang=lang | default(value='sk')) }}</a>
<form method="post" action="/logout" hx-boost="false" class="border-t border-outline dark:border-outline-dark">
<button type="submit" role="menuitem"
class="block w-full px-4 py-2 text-left text-sm font-medium text-danger transition hover:bg-primary/5 focus-visible:bg-primary/10 focus-visible:outline-hidden">{{ t(key="logout", lang=lang | default(value='sk')) }}</button>
</form>
<div x-data="{ open: false }"
x-on:keydown.esc.window="open = false"
class="relative flex items-center">
<!-- Toggle Button: plain circular avatar (name/type live in the dropdown) -->
<button type="button" x-on:click="open = !open"
x-bind:aria-expanded="open" aria-haspopup="true" aria-controls="userMenu"
aria-label="{{ t(key='nav-account', lang=lang | default(value='sk')) }}"
class="flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full border border-primary bg-primary text-sm font-bold tracking-wider text-on-primary/90 transition hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-primary-dark dark:bg-primary-dark dark:text-on-primary-dark/90 dark:focus-visible:outline-primary-dark">
{%- if _initials %}{{ _initials }}{% else %}{{ _person_icon | safe }}{% endif -%}
</button>
<!-- User Dropdown -->
<ul x-cloak x-show="open" x-transition.opacity style="display: none"
x-on:click.outside="open = false"
id="userMenu" class="absolute right-0 top-12 flex w-60 min-w-48 flex-col overflow-hidden rounded-radius border border-outline bg-surface-alt py-1.5 shadow-lg dark:border-outline-dark dark:bg-surface-dark-alt" role="menu">
<li class="border-b border-outline dark:border-outline-dark">
<div class="flex items-center gap-3 px-4 py-2.5">
<span class="flex size-11 shrink-0 items-center justify-center overflow-hidden rounded-full border border-primary bg-primary text-base font-bold tracking-wider text-on-primary/90 dark:border-primary-dark dark:bg-primary-dark dark:text-on-primary-dark/90">
{%- if _initials %}{{ _initials }}{% else %}{{ _person_icon | safe }}{% endif -%}
</span>
<div class="flex min-w-0 flex-col">
<span class="truncate text-sm font-medium text-on-surface-strong dark:text-on-surface-dark-strong">{{ _name }}</span>
<p class="truncate text-xs text-on-surface dark:text-on-surface-dark">{{ _type_label }}</p>
</div>
</div>
</li>
<li><a href="/account/orders" data-nav="/account/orders" role="menuitem" class="block bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/10 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong">{{ t(key="account-orders", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/account/profile" data-nav="/account/profile" role="menuitem" class="block bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/10 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong">{{ t(key="profile-title", lang=lang | default(value='sk')) }}</a></li>
<li><a href="/account/password" data-nav="/account/password" role="menuitem" class="block bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/10 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong">{{ t(key="account-change-password", lang=lang | default(value='sk')) }}</a></li>
<li><form method="post" action="/logout" hx-boost="false"><button type="submit" role="menuitem" class="block w-full bg-surface-alt px-4 py-2 text-left text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/10 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong">{{ t(key="logout", lang=lang | default(value='sk')) }}</button></form></li>
</ul>
</div>