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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -57,6 +57,9 @@
</script> </script>
<link href="/static/css/app.css?v=2026-06-16" rel="stylesheet" type="text/css"> <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> <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> <script defer src="/static/vendor/alpine/alpinejs-3.14.9.min.js"></script>
</head> </head>
<body hx-boost="true" <body hx-boost="true"
@@ -95,8 +98,13 @@
<!-- right side: cart + settings + mobile toggle --> <!-- right side: cart + settings + mobile toggle -->
<div class="ml-auto flex items-center gap-1"> <div class="ml-auto flex items-center gap-1">
<!-- cart with live item-count badge read from the `cart` cookie --> <!-- customer profile dropdown (avatar + name + account type) -->
<a href="/cart" data-nav="/cart" {% 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-data="{ count: 0 }"
x-init="count = cartCount(); ['htmx:afterSwap', 'htmx:afterRequest'].forEach(function (e) { window.addEventListener(e, function () { count = cartCount() }) })" 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')) }}" 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" <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> 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> </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 --> <!-- settings (language + theme) dropdown -->
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative"> <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. {# Customer profile dropdown in the storefront navbar.
Trigger shows an initials avatar, the customer's name and their account type
(personal / company); clicking opens a quick-navigation menu.
The host template provides the wrapper Penguin UI markup (navbar/with-user-profile.html dropdown + avatar rendered as
<div x-data="{ open: false }" @keydown.escape="open = false" class="relative">. #} 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 _name = customer_name | default(value='') | trim %}
{% set _parts = _name | split(pat=' ') %} {% set _parts = _name | split(pat=' ') %}
{% set _initials = _parts.0 | truncate(length=1, end='') | upper %} {% 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 _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 %} {% 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" {% 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>' %}
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>
<div x-show="open" x-cloak @click.outside="open = false" x-transition.origin.top.right <div x-data="{ open: false }"
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" x-on:keydown.esc.window="open = false"
role="menu"> class="relative flex items-center">
<div class="flex items-center gap-3 border-b border-outline px-4 py-3 dark:border-outline-dark"> <!-- Toggle Button: plain circular avatar (name/type live in the dropdown) -->
<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> <button type="button" x-on:click="open = !open"
<span class="flex min-w-0 flex-col leading-tight"> x-bind:aria-expanded="open" aria-haspopup="true" aria-controls="userMenu"
<span class="truncate text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ _name }}</span> aria-label="{{ t(key='nav-account', lang=lang | default(value='sk')) }}"
<span class="truncate text-xs text-on-surface/60 dark:text-on-surface-dark/60">{{ _type_label }}</span> 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">
</span> {%- if _initials %}{{ _initials }}{% else %}{{ _person_icon | safe }}{% endif -%}
</div> </button>
<a href="/account/orders" data-nav="/account/orders" role="menuitem" <!-- User Dropdown -->
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> <ul x-cloak x-show="open" x-transition.opacity style="display: none"
<a href="/account/profile" data-nav="/account/profile" role="menuitem" x-on:click.outside="open = false"
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> 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">
<a href="/account/password" data-nav="/account/password" role="menuitem" <li class="border-b border-outline dark:border-outline-dark">
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> <div class="flex items-center gap-3 px-4 py-2.5">
<form method="post" action="/logout" hx-boost="false" class="border-t border-outline dark:border-outline-dark"> <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">
<button type="submit" role="menuitem" {%- if _initials %}{{ _initials }}{% else %}{{ _person_icon | safe }}{% endif -%}
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> </span>
</form> <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> </div>