Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5001e46866 | ||
|
|
5dcc8028b2 | ||
|
|
3df88b4cee | ||
|
|
ba02930454 | ||
|
|
1fc8796389 | ||
|
|
e5ec2a2de6 | ||
|
|
70908cba8b | ||
|
|
6b3739d629 | ||
|
|
f3b920d4b2 | ||
|
|
caec8b4fb3 | ||
|
|
d6d4f19010 | ||
|
|
77066d660c | ||
|
|
3aa5f63264 | ||
|
|
f04691a733 | ||
|
|
6dd1164c65 | ||
|
|
5f7ddce6a7 |
@@ -39,17 +39,24 @@
|
||||
@theme {
|
||||
/* light mode — Catppuccin Latte (https://catppuccin.com/palette)
|
||||
* Base #eff1f5, Mantle #e6e9ef, Surface1 #bcc0cc, Subtext1 #5c5f77,
|
||||
* Subtext0 #6c6f85, Text #4c4f69, Blue #1e66f5. */
|
||||
* Subtext0 #6c6f85, Text #4c4f69. Primary is the KOMPRESS logo blue
|
||||
* (sampled from logo.jpg) rather than the Latte blue. */
|
||||
--color-surface: #eff1f5; /* Base */
|
||||
--color-surface-alt: #e6e9ef; /* Mantle */
|
||||
--color-on-surface: #5c5f77; /* Subtext1 */
|
||||
--color-on-surface-strong: #4c4f69; /* Text */
|
||||
--color-primary: #1e66f5; /* Blue */
|
||||
--color-primary: #1600ff; /* KOMPRESS logo blue */
|
||||
--color-on-primary: #eff1f5; /* Base */
|
||||
--color-secondary: #6c6f85; /* Subtext0 */
|
||||
--color-on-secondary: #eff1f5; /* Base */
|
||||
--color-outline: #bcc0cc; /* Surface1 */
|
||||
--color-outline-strong: #4c4f69; /* Text */
|
||||
/* CTA: solid fill for large/filled buttons + the contact block. The vivid
|
||||
* logo blue (--color-primary) is reserved for tiny accents (links, hover
|
||||
* tints, badges); the CTA color is the logo blue itself, just with alpha so
|
||||
* big buttons read as a translucent tint rather than the full vivid fill. */
|
||||
--color-cta: rgba(22, 0, 255, 0.85);
|
||||
--color-on-cta: #eff1f5;
|
||||
|
||||
/* dark mode — Gruvbox dark palette (https://github.com/morhetz/gruvbox)
|
||||
* bg0 #282828, bg1 #3c3836, bg2 #504945, fg0 #fbf1c7, fg1 #ebdbb2,
|
||||
@@ -64,6 +71,9 @@
|
||||
--color-on-secondary-dark: #1d2021; /* bg0_h */
|
||||
--color-outline-dark: #504945; /* bg2 */
|
||||
--color-outline-dark-strong: #bdae93; /* fg3 */
|
||||
/* CTA in dark mode tracks the existing primary so dark buttons are unchanged. */
|
||||
--color-cta-dark: #83a598; /* = primary-dark */
|
||||
--color-on-cta-dark: #1d2021; /* = on-primary-dark */
|
||||
|
||||
/* shared status colors (same in both modes) */
|
||||
--color-info: var(--color-sky-500);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
brand = Kompress eshop
|
||||
brand = WWW.KOMPRESS.SK, s.r.o.
|
||||
hello-world = Hello world!
|
||||
meta-description = Kompress eshop
|
||||
meta-description = Manufacturer and distributor of medical aids and supplies
|
||||
nav-home = Home
|
||||
nav-about = About
|
||||
nav-blog = Blog
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
brand = Kompress eshop
|
||||
brand = WWW.KOMPRESS.SK, s.r.o.
|
||||
hello-world = Hello world!
|
||||
meta-description = Kompress eshop
|
||||
meta-description = Manufacturer and distributor of medical aids and supplies
|
||||
nav-home = Home
|
||||
nav-about = About
|
||||
nav-blog = Blog
|
||||
@@ -218,7 +218,7 @@ option-label = Option label
|
||||
optional = optional
|
||||
stock-untracked-hint = Leave blank = available without stock tracking
|
||||
available = Available
|
||||
choose-option = Choose an option
|
||||
choose-option = Options
|
||||
from-price = from { $price }
|
||||
admin-discounts = Discounts
|
||||
admin-discounts-desc = Set discounted product prices. A discount shows up as a sale in the shop.
|
||||
@@ -300,7 +300,7 @@ position-hint = Sort order in the menu (lowest first). Leave blank to add it las
|
||||
parent-category = Parent category
|
||||
no-parent = — None (top level) —
|
||||
quantity = Quantity
|
||||
add-to-cart = Add to cart
|
||||
add-to-cart = To cart
|
||||
cart-added = Added to cart
|
||||
in-stock = In stock
|
||||
out-of-stock = Out of stock
|
||||
@@ -523,7 +523,21 @@ footer-account = Account
|
||||
footer-contact = Contact
|
||||
footer-terms = Terms and conditions
|
||||
footer-about = About our company
|
||||
footer-stores = Our stores
|
||||
footer-stores = Where it's made
|
||||
home-stores-photo = Our production facility
|
||||
home-stores-discover = Step inside our workshop ›
|
||||
page-stores-intro = This is our own facility where our medical aids and supplies are produced.
|
||||
page-stores-facility = Production facility
|
||||
page-stores-address-label = Facility address
|
||||
page-stores-address = Nádražná 328/62, 015 01 Rajec nad Rajčankou
|
||||
page-stores-photo-caption = Our production facility in Rajec nad Rajčankou
|
||||
page-stores-map = Where to find us
|
||||
page-stores-map-open = Open in Google Maps ›
|
||||
home-bestsellers = Best sellers
|
||||
home-bestsellers-all = All best sellers ›
|
||||
home-contact-title = Contact us
|
||||
home-contact-text = Our hotline is available 24/7. We're happy to help you choose.
|
||||
home-contact-cta = Contact the hotline
|
||||
footer-shipping = Shipping and payment
|
||||
footer-orders = My orders
|
||||
footer-email = info@kompress.sk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
brand = Kompress eshop
|
||||
brand = WWW.KOMPRESS.SK, s.r.o.
|
||||
hello-world = Ahoj svet!
|
||||
meta-description = Kompress eshop
|
||||
meta-description = Výrobca a distribútor zdravotníckych pomôcok a potrieb
|
||||
nav-home = Domov
|
||||
nav-about = O mne
|
||||
nav-blog = Blog
|
||||
@@ -218,7 +218,7 @@ option-label = Označenie možnosti
|
||||
optional = voliteľné
|
||||
stock-untracked-hint = Nechajte prázdne = dostupné bez sledovania zásob
|
||||
available = Dostupné
|
||||
choose-option = Vyberte možnosť
|
||||
choose-option = Options
|
||||
from-price = od { $price }
|
||||
admin-discounts = Zľavy
|
||||
admin-discounts-desc = Nastavte zľavnené ceny produktov. Zľava sa v obchode zobrazí ako akcia.
|
||||
@@ -300,7 +300,7 @@ position-hint = Poradie v menu (najnižšie ako prvé). Nechajte prázdne a prid
|
||||
parent-category = Nadradená kategória
|
||||
no-parent = — Žiadna (najvyššia úroveň) —
|
||||
quantity = Množstvo
|
||||
add-to-cart = Pridať do košíka
|
||||
add-to-cart = Do košíka
|
||||
cart-added = Pridané do košíka
|
||||
in-stock = Na sklade
|
||||
out-of-stock = Vypredané
|
||||
@@ -523,7 +523,21 @@ footer-account = Účet
|
||||
footer-contact = Kontakt
|
||||
footer-terms = Obchodné podmienky
|
||||
footer-about = O našej spoločnosti
|
||||
footer-stores = Naše obchody
|
||||
footer-stores = Kde to vzniká
|
||||
home-stores-photo = Naša výrobná prevádzka
|
||||
home-stores-discover = Nahliadnite do výroby ›
|
||||
page-stores-intro = Toto je naša vlastná prevádzka, kde vyrábame naše zdravotnícke pomôcky a potreby.
|
||||
page-stores-facility = Výrobná prevádzka
|
||||
page-stores-address-label = Adresa prevádzky
|
||||
page-stores-address = Nádražná 328/62, 015 01 Rajec nad Rajčankou
|
||||
page-stores-photo-caption = Naša výrobná prevádzka v Rajci nad Rajčankou
|
||||
page-stores-map = Kde nás nájdete
|
||||
page-stores-map-open = Otvoriť v Google Mapách ›
|
||||
home-bestsellers = Najpredávanejšie
|
||||
home-bestsellers-all = Všetko najpredávanejšie ›
|
||||
home-contact-title = Kontaktujte nás
|
||||
home-contact-text = Naša horúca linka je dostupná 24/7. Radi vám poradíme s výberom.
|
||||
home-contact-cta = Kontaktujte hotline
|
||||
footer-shipping = Doprava a platba
|
||||
footer-orders = Moje objednávky
|
||||
footer-email = info@kompress.sk
|
||||
|
||||
File diff suppressed because one or more lines are too long
BIN
assets/static/img/logo.jpg
Normal file
BIN
assets/static/img/logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
BIN
assets/static/img/store.jpg
Normal file
BIN
assets/static/img/store.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -92,20 +92,15 @@
|
||||
|
||||
<header
|
||||
class="sticky top-0 z-30 border-b border-outline bg-surface/95 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95">
|
||||
<nav x-data="{ mobile: false }" class="mx-auto flex max-w-7xl items-center gap-3 px-4 py-3 sm:gap-4">
|
||||
<nav class="mx-auto flex max-w-7xl items-center gap-3 px-4 py-3 sm:gap-4">
|
||||
<!-- category sidebar toggle (mobile only) -->
|
||||
{% set hamburger_icon = ui::icon(name="hamburger", size="size-6") %}
|
||||
{{ ui::icon_button(aria_label=t(key='categories', lang=lang | default(value='sk')), attrs='@click="cats = !cats" :aria-expanded="cats"', extra="lg:hidden", icon=hamburger_icon) }}
|
||||
|
||||
<!-- logo lockup: blue cross tile + wordmark + medical-supplies subtitle -->
|
||||
<a href="/" class="flex shrink-0 items-center gap-2.5">
|
||||
<span class="inline-flex size-9 items-center justify-center rounded-radius bg-primary text-on-primary dark:bg-primary-dark dark:text-on-primary-dark">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="10" y="3" width="4" height="18" rx="1.5"></rect><rect x="3" y="10" width="18" height="4" rx="1.5"></rect></svg>
|
||||
</span>
|
||||
<span class="flex flex-col leading-none">
|
||||
<span class="text-lg font-extrabold tracking-tight text-primary dark:text-primary-dark">{{ t(key="brand", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="hidden text-[10px] font-semibold uppercase tracking-wider text-on-surface/50 sm:block dark:text-on-surface-dark/50">{{ t(key="brand-subtitle", lang=lang | default(value='sk')) }}</span>
|
||||
</span>
|
||||
<!-- real KOMPRESS logo from www.e-shop.kompress.sk (hidden on mobile;
|
||||
the category drawer carries navigation there) -->
|
||||
<a href="/" class="hidden shrink-0 items-center sm:flex">
|
||||
<img src="/static/img/logo.jpg" alt="{{ t(key='brand', lang=lang | default(value='sk')) }}" width="260" height="52" class="h-8 w-auto dark:rounded-radius dark:bg-white dark:px-1.5 dark:py-0.5" />
|
||||
</a>
|
||||
|
||||
<!-- in-header search → existing GET /search (q param). Only on the home
|
||||
@@ -119,7 +114,7 @@
|
||||
placeholder="{{ t(key='search-placeholder', lang=lang | default(value='sk')) }}"
|
||||
aria-label="{{ t(key='search-placeholder', lang=lang | default(value='sk')) }}"
|
||||
class="min-w-0 flex-1 border-0 bg-surface-alt px-2.5 py-2.5 text-sm text-on-surface placeholder:text-on-surface/50 focus:outline-none dark:bg-surface-dark-alt dark:text-on-surface-dark dark:placeholder:text-on-surface-dark/50" />
|
||||
<button type="submit" class="shrink-0 bg-primary px-5 text-sm font-bold text-on-primary transition hover:opacity-90 dark:bg-primary-dark dark:text-on-primary-dark">{{ t(key="search-button", lang=lang | default(value='sk')) }}</button>
|
||||
<button type="submit" class="shrink-0 bg-cta px-5 text-sm font-bold text-on-cta transition hover:opacity-90 dark:bg-cta-dark dark:text-on-cta-dark">{{ t(key="search-button", lang=lang | default(value='sk')) }}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
@@ -140,7 +135,7 @@
|
||||
<!-- account area: admin quick links / customer profile dropdown /
|
||||
guest two-line "Vitajte · Prihláste sa" button (Kompress design) -->
|
||||
{% if logged_in_admin %}
|
||||
<div class="hidden items-center gap-3 sm:flex">
|
||||
<div class="flex items-center gap-3">
|
||||
{{ ui::nav_link(label=t(key="admin-title", lang=lang | default(value='sk')), href="/admin/dashboard", data_nav="/admin", variant="warning", attrs='hx-boost="false"') }}
|
||||
<form method="post" action="/logout" hx-boost="false">
|
||||
{{ ui::csrf_field() }}
|
||||
@@ -150,9 +145,9 @@
|
||||
{% elif logged_in_customer %}
|
||||
{% include "partials/profile_menu.html" %}
|
||||
{% else %}
|
||||
<a href="/login" data-nav="/login" class="hidden items-center gap-2.5 rounded-radius px-2.5 py-1.5 text-on-surface transition hover:bg-surface-alt sm:inline-flex dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
|
||||
<a href="/login" data-nav="/login" class="inline-flex items-center gap-2.5 rounded-radius px-2.5 py-1.5 text-on-surface transition hover:bg-surface-alt dark:text-on-surface-dark dark:hover:bg-surface-dark-alt">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" class="size-5 shrink-0" aria-hidden="true"><circle cx="12" cy="8" r="4"></circle><path d="M5 20a7 7 0 0 1 14 0"></path></svg>
|
||||
<span class="flex flex-col items-start leading-tight">
|
||||
<span class="hidden flex-col items-start leading-tight sm:flex">
|
||||
<span class="text-[11px] text-on-surface/50 dark:text-on-surface-dark/50">{{ t(key="welcome", lang=lang | default(value='sk')) }}</span>
|
||||
<span class="text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="nav-login", lang=lang | default(value='sk')) }}</span>
|
||||
</span>
|
||||
@@ -199,54 +194,7 @@
|
||||
|
||||
<!-- settings (language + theme) dropdown (self-contained Alpine state) -->
|
||||
{% include "partials/settings_dropdown.html" %}
|
||||
|
||||
<!-- mobile hamburger — Penguin animated icon swap (bars ↔ X), kept in
|
||||
our ghost-square icon-button shell for consistency with cart/gear -->
|
||||
<button type="button" @click="mobile = !mobile" :aria-expanded="mobile" aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
|
||||
class="inline-flex size-9 shrink-0 items-center justify-center rounded-radius bg-transparent text-secondary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary active:opacity-100 active:outline-offset-0 md:hidden dark:text-secondary-dark dark:focus-visible:outline-secondary-dark">
|
||||
{{ ui::icon(name="hamburger", size="size-6", attrs='x-show="!mobile"') }}
|
||||
{{ ui::icon(name="close", size="size-6", attrs='x-cloak x-show="mobile"') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- mobile menu panel — Penguin sidebar-style menu rows (hover:bg-primary/5,
|
||||
underline focus), active state via data-nav + markActiveNav() -->
|
||||
<ul x-show="mobile" x-cloak @click.outside="mobile = false" x-transition
|
||||
class="absolute inset-x-0 top-full mx-4 mt-2 flex flex-col gap-1 rounded-radius border border-outline bg-surface p-2 shadow-lg md:hidden dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
{% if on_home | default(value=false) %}
|
||||
<li class="mb-1">
|
||||
<form action="/search" method="get" role="search" class="flex overflow-hidden rounded-radius border border-outline dark:border-outline-dark">
|
||||
<input type="search" name="q" autocomplete="off"
|
||||
placeholder="{{ t(key='search-placeholder', lang=lang | default(value='sk')) }}"
|
||||
aria-label="{{ t(key='search-placeholder', lang=lang | default(value='sk')) }}"
|
||||
class="min-w-0 flex-1 border-0 bg-surface-alt px-3 py-2 text-sm text-on-surface placeholder:text-on-surface/50 focus:outline-none dark:bg-surface-dark-alt dark:text-on-surface-dark dark:placeholder:text-on-surface-dark/50" />
|
||||
<button type="submit" class="shrink-0 bg-primary px-4 text-sm font-bold text-on-primary dark:bg-primary-dark dark:text-on-primary-dark">{{ t(key="search-button", lang=lang | default(value='sk')) }}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li><a href="/" data-nav="/" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-primary focus:outline-hidden focus-visible:underline aria-[current=page]:font-semibold aria-[current=page]:bg-primary/10 aria-[current=page]:text-primary dark:text-on-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="nav-home", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/shop" data-nav="/shop" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-primary focus:outline-hidden focus-visible:underline aria-[current=page]:font-semibold aria-[current=page]:bg-primary/10 aria-[current=page]:text-primary dark:text-on-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="nav-shop", lang=lang | default(value='sk')) }}</a></li>
|
||||
{% if logged_in_admin %}
|
||||
<li><a href="/admin/dashboard" hx-boost="false" data-nav="/admin" class="block rounded-radius px-3 py-2 text-sm font-medium text-warning underline-offset-2 transition hover:bg-primary/5 focus:outline-hidden focus-visible:underline">{{ t(key="admin-title", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li>
|
||||
<form method="post" action="/logout" hx-boost="false">
|
||||
{{ ui::csrf_field() }}
|
||||
<button type="submit" class="block w-full rounded-radius px-3 py-2 text-left text-sm font-medium text-danger underline-offset-2 transition hover:bg-primary/5 focus:outline-hidden focus-visible:underline">{{ t(key="logout", lang=lang | default(value='sk')) }}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% elif logged_in_customer %}
|
||||
<li><a href="/account/profile" data-nav="/account" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-primary focus:outline-hidden focus-visible:underline aria-[current=page]:font-semibold aria-[current=page]:bg-primary/10 aria-[current=page]:text-primary dark:text-on-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="nav-profile", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li>
|
||||
<form method="post" action="/logout" hx-boost="false">
|
||||
{{ ui::csrf_field() }}
|
||||
<button type="submit" class="block w-full rounded-radius px-3 py-2 text-left text-sm font-medium text-danger underline-offset-2 transition hover:bg-primary/5 focus:outline-hidden focus-visible:underline">{{ t(key="logout", lang=lang | default(value='sk')) }}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% else %}
|
||||
<li><a href="/login" data-nav="/login" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-primary focus:outline-hidden focus-visible:underline aria-[current=page]:font-semibold aria-[current=page]:bg-primary/10 aria-[current=page]:text-primary dark:text-on-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="nav-login", lang=lang | default(value='sk')) }}</a></li>
|
||||
<li><a href="/register" data-nav="/register" class="block rounded-radius px-3 py-2 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-primary focus:outline-hidden focus-visible:underline aria-[current=page]:font-semibold aria-[current=page]:bg-primary/10 aria-[current=page]:text-primary dark:text-on-surface-dark dark:hover:text-primary-dark dark:aria-[current=page]:text-primary-dark">{{ t(key="nav-register", lang=lang | default(value='sk')) }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
|
||||
@@ -12,15 +12,45 @@
|
||||
{% endblock breadcrumbs %}
|
||||
|
||||
{% block content %}
|
||||
{% set L = lang | default(value='sk') %}
|
||||
{# Home layout adapted from the Kompress design mockup: the left "Kategórie"
|
||||
column is already supplied by base.html's #category-sidebar, so the main
|
||||
area is split into a featured product grid + a right rail (bestsellers /
|
||||
our stores / contact). All colors use the design tokens so light + dark
|
||||
both work; the brand accent is the medical blue set in app.css. #}
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_19rem]">
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_19rem] lg:grid-rows-[auto_1fr] lg:items-start">
|
||||
|
||||
<!-- bestsellers (reuses the featured products). DOM-first so it stacks above
|
||||
the product grid on mobile; placed in the right rail's top cell on lg. -->
|
||||
{% if products | length > 0 %}
|
||||
<section class="overflow-hidden rounded-radius border border-outline bg-surface-alt lg:col-start-2 lg:row-start-1 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<h2 class="border-b border-outline px-4 py-3.5 text-xs font-bold uppercase tracking-wider text-on-surface-strong dark:border-outline-dark dark:text-on-surface-dark-strong">{{ t(key="home-bestsellers", lang=L) }}</h2>
|
||||
<ol class="p-2">
|
||||
{% for product in products | slice(end=5) %}
|
||||
<li>
|
||||
<a href="/shop/{{ product.slug }}" class="flex items-center gap-3 rounded-radius px-2 py-2 transition hover:bg-primary/5">
|
||||
<span class="inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-primary/10 text-xs font-extrabold text-primary dark:bg-primary-dark/15 dark:text-primary-dark">{{ loop.index }}</span>
|
||||
<span class="flex size-11 shrink-0 items-center justify-center overflow-hidden rounded-md border border-outline bg-surface dark:border-outline-dark dark:bg-surface-dark">
|
||||
{% if product.image %}
|
||||
<img src="/images/{{ product.image }}" alt="{{ product.name }}" class="size-full object-cover">
|
||||
{% else %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" class="text-on-surface/30 dark:text-on-surface-dark/30"><rect x="3" y="4" width="18" height="16" rx="2"></rect><circle cx="8.5" cy="9" r="1.6"></circle><path d="M21 16l-5-5L5 20"></path></svg>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="flex min-w-0 flex-col gap-0.5">
|
||||
<span class="line-clamp-2 text-[13px] font-semibold leading-tight text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</span>
|
||||
<span class="text-sm font-extrabold text-primary dark:text-primary-dark">{% if product.has_options %}{{ t(key="from-price", price=product.price, lang=lang | default(value='sk')) }}{% else %}{{ product.price }}{% endif %} {{ currency_symbol }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<a href="/shop" class="block border-t border-outline px-4 py-3 text-center text-[13px] font-semibold text-primary transition hover:bg-primary/5 dark:border-outline-dark dark:text-primary-dark">{{ t(key="home-bestsellers-all", lang=L) }}</a>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- center column -->
|
||||
<div class="flex min-w-0 flex-col gap-6">
|
||||
<div class="flex min-w-0 flex-col gap-6 lg:col-start-1 lg:row-span-2 lg:row-start-1">
|
||||
<!-- hero / heading -->
|
||||
<section>
|
||||
<h1 class="text-3xl font-extrabold tracking-tight text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="shop-title", lang=lang | default(value='sk')) }}</h1>
|
||||
@@ -34,7 +64,12 @@
|
||||
<h2 class="text-xl font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="nav-shop", lang=lang | default(value='sk')) }}</h2>
|
||||
<a href="/shop" class="text-sm font-semibold text-primary dark:text-primary-dark">{{ t(key="cart-continue", lang=lang | default(value='sk')) }} →</a>
|
||||
</div>
|
||||
<div x-data="{ view: 'grid' }" class="grid grid-cols-2 gap-4 sm:grid-cols-3">
|
||||
<div x-data="{ view: localStorage.getItem('shopView') === 'grid' ? 'grid' : 'list' }"
|
||||
x-init="$watch('view', v => localStorage.setItem('shopView', v))"
|
||||
{# Fixed-width cards (14rem), identical to the shop. Cards never stretch;
|
||||
the column just fits as many as it can (home fewer, shop more), so a
|
||||
card is the exact same width on both pages regardless of column count. #}
|
||||
:class="view === 'list' ? 'flex flex-col gap-5' : 'grid grid-cols-2 gap-5 sm:grid-cols-[repeat(auto-fill,14rem)] sm:justify-center'">
|
||||
{% for product in products %}
|
||||
{% include "shop/_card.html" %}
|
||||
{% endfor %}
|
||||
@@ -49,55 +84,28 @@
|
||||
</div>
|
||||
|
||||
<!-- right rail -->
|
||||
<aside class="flex flex-col gap-5">
|
||||
|
||||
<!-- bestsellers (reuses the featured products) -->
|
||||
{% if products | length > 0 %}
|
||||
<section class="overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<h2 class="border-b border-outline px-4 py-3.5 text-xs font-bold uppercase tracking-wider text-on-surface-strong dark:border-outline-dark dark:text-on-surface-dark-strong">Najpredávanejšie</h2>
|
||||
<ol class="p-2">
|
||||
{% for product in products | slice(end=5) %}
|
||||
<li>
|
||||
<a href="/shop/{{ product.slug }}" class="flex items-center gap-3 rounded-radius px-2 py-2 transition hover:bg-primary/5">
|
||||
<span class="inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-primary/10 text-xs font-extrabold text-primary dark:bg-primary-dark/15 dark:text-primary-dark">{{ loop.index }}</span>
|
||||
<span class="flex size-11 shrink-0 items-center justify-center overflow-hidden rounded-md border border-outline bg-surface dark:border-outline-dark dark:bg-surface-dark">
|
||||
{% if product.image %}
|
||||
<img src="/images/{{ product.image }}" alt="{{ product.name }}" class="size-full object-cover">
|
||||
{% else %}
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" class="text-on-surface/30 dark:text-on-surface-dark/30"><rect x="3" y="4" width="18" height="16" rx="2"></rect><circle cx="8.5" cy="9" r="1.6"></circle><path d="M21 16l-5-5L5 20"></path></svg>
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="flex min-w-0 flex-col gap-0.5">
|
||||
<span class="line-clamp-2 text-[13px] font-semibold leading-tight text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</span>
|
||||
<span class="text-sm font-extrabold text-primary dark:text-primary-dark">{% if product.has_options %}{{ t(key="from-price", price=product.price, lang=lang | default(value='sk')) }}{% else %}{{ product.price }}{% endif %} {{ currency_symbol }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<a href="/shop" class="block border-t border-outline px-4 py-3 text-center text-[13px] font-semibold text-primary transition hover:bg-primary/5 dark:border-outline-dark dark:text-primary-dark">Všetko najpredávanejšie ›</a>
|
||||
</section>
|
||||
{% endif %}
|
||||
<aside class="flex flex-col gap-5 lg:col-start-2 lg:row-start-2">
|
||||
|
||||
<!-- our stores (static) -->
|
||||
<section class="overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<h2 class="border-b border-outline px-4 py-3.5 text-xs font-bold uppercase tracking-wider text-on-surface-strong dark:border-outline-dark dark:text-on-surface-dark-strong">Naše obchody</h2>
|
||||
<h2 class="border-b border-outline px-4 py-3.5 text-xs font-bold uppercase tracking-wider text-on-surface-strong dark:border-outline-dark dark:text-on-surface-dark-strong">{{ t(key="footer-stores", lang=L) }}</h2>
|
||||
<div class="p-3.5">
|
||||
<div class="flex h-28 items-center justify-center rounded-radius border border-outline bg-surface text-sm text-on-surface/50 dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark/40">Foto predajne</div>
|
||||
<a href="/predajne" class="mt-3 inline-block text-sm font-bold text-primary transition hover:underline dark:text-primary-dark">Objaviť naše obchody ›</a>
|
||||
<img src="/static/img/store.jpg" alt="{{ t(key='home-stores-photo', lang=L) }}" width="142" height="115" loading="lazy"
|
||||
class="h-28 w-full rounded-radius border border-outline object-cover dark:border-outline-dark" />
|
||||
<a href="/predajne" class="mt-3 inline-block text-sm font-bold text-primary transition hover:underline dark:text-primary-dark">{{ t(key="home-stores-discover", lang=L) }}</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- contact CTA (static, brand blue) -->
|
||||
<section class="overflow-hidden rounded-radius bg-primary text-on-primary dark:bg-primary-dark dark:text-on-primary-dark">
|
||||
<section class="overflow-hidden rounded-radius bg-cta text-on-cta dark:bg-cta-dark dark:text-on-cta-dark">
|
||||
<div class="p-5">
|
||||
<div class="text-xs font-bold uppercase tracking-wider opacity-80">Kontaktujte nás</div>
|
||||
<p class="mt-2.5 text-sm leading-relaxed opacity-90">Naša horúca linka je dostupná 24/7. Radi vám poradíme s výberom.</p>
|
||||
<div class="text-xs font-bold uppercase tracking-wider opacity-80">{{ t(key="home-contact-title", lang=L) }}</div>
|
||||
<p class="mt-2.5 text-sm leading-relaxed opacity-90">{{ t(key="home-contact-text", lang=L) }}</p>
|
||||
<a href="tel:+421903410476" class="mt-3.5 flex items-center gap-2.5 text-xl font-extrabold tracking-tight">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M5 4h4l2 5-3 2a12 12 0 0 0 5 5l2-3 5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2Z"></path></svg>
|
||||
+421 903 410 476
|
||||
</a>
|
||||
<a href="tel:+421903410476" class="mt-3.5 block w-full rounded-radius bg-surface px-4 py-3 text-center text-sm font-bold text-primary transition hover:opacity-90 dark:bg-surface-dark dark:text-primary-dark">Kontaktujte hotline</a>
|
||||
<a href="tel:+421903410476" class="mt-3.5 block w-full rounded-radius bg-surface px-4 py-3 text-center text-sm font-bold text-cta transition hover:opacity-90 dark:bg-surface-dark dark:text-cta-dark">{{ t(key="home-contact-cta", lang=L) }}</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro button(label, variant="primary", type="button", href="", attrs="", extra="", icon="", size="px-4 py-2 text-sm") -%}
|
||||
{% macro button(label, variant="primary", type="button", href="", attrs="", extra="", icon="", size="px-4 py-2 text-sm", nowrap=true) -%}
|
||||
{%- if variant == "secondary" -%}{% set cls = "border border-secondary bg-secondary text-on-secondary focus-visible:outline-secondary dark:border-secondary-dark dark:bg-secondary-dark dark:text-on-secondary-dark dark:focus-visible:outline-secondary-dark" -%}
|
||||
{%- elif variant == "danger" -%}{% set cls = "border border-danger bg-danger text-on-danger focus-visible:outline-danger dark:bg-danger dark:border-danger dark:text-on-danger dark:focus-visible:outline-danger" -%}
|
||||
{%- elif variant == "success" -%}{% set cls = "border border-success bg-success text-on-success focus-visible:outline-success dark:bg-success dark:border-success dark:text-on-success dark:focus-visible:outline-success" -%}
|
||||
@@ -49,9 +49,9 @@
|
||||
{%- elif variant == "ghost-primary" -%}{% set cls = "bg-transparent text-primary focus-visible:outline-primary dark:text-primary-dark dark:focus-visible:outline-primary-dark" -%}
|
||||
{%- elif variant == "ghost-secondary" -%}{% set cls = "bg-transparent text-secondary focus-visible:outline-secondary dark:text-secondary-dark dark:focus-visible:outline-secondary-dark" -%}
|
||||
{%- elif variant == "ghost-danger" -%}{% set cls = "bg-transparent text-danger focus-visible:outline-danger dark:text-danger dark:focus-visible:outline-danger" -%}
|
||||
{%- else -%}{% set cls = "border border-primary bg-primary text-on-primary focus-visible:outline-primary dark:border-primary-dark dark:bg-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark" -%}
|
||||
{%- else -%}{% set cls = "border border-cta bg-cta text-on-cta focus-visible:outline-cta dark:border-cta-dark dark:bg-cta-dark dark:text-on-cta-dark dark:focus-visible:outline-cta-dark" -%}
|
||||
{%- endif -%}
|
||||
{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %} class="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius {{ size }} text-center font-medium tracking-wide transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 {{ cls }} {{ extra }}" {{ attrs | safe }}>{{ icon | safe }}{{ label }}</{% if href %}a{% else %}button{% endif %}>
|
||||
{% if nowrap %}{% set wrap = "whitespace-nowrap" %}{% else %}{% set wrap = "text-balance" %}{% endif %}{% if href %}<a href="{{ href }}"{% else %}<button type="{{ type }}"{% endif %} class="inline-flex items-center justify-center gap-2 {{ wrap }} rounded-radius {{ size }} text-center font-medium tracking-wide transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 {{ cls }} {{ extra }}" {{ attrs | safe }}>{{ icon | safe }}{{ label }}</{% if href %}a{% else %}button{% endif %}>
|
||||
{%- endmacro button %}
|
||||
|
||||
{# Icon-only button (square). Penguin ghost treatment (bg-transparent,
|
||||
|
||||
@@ -57,6 +57,38 @@
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
{% elif page == "stores" %}
|
||||
{# Production facility (not a retail store): intro, Google map on top, then a
|
||||
small facility photo next to the address card. #}
|
||||
<p class="text-lg text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="page-stores-intro", lang=L) }}</p>
|
||||
|
||||
{# Google Map of the production facility. Embedded via the keyless Maps embed
|
||||
(centered on the geocoded coords); a static PNG would need a Maps Static
|
||||
API key. The header links out to the full map. #}
|
||||
<section class="overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<div class="flex items-center justify-between gap-3 border-b border-outline px-4 py-3 dark:border-outline-dark">
|
||||
<h2 class="text-sm font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="page-stores-map", lang=L) }}</h2>
|
||||
<a href="https://www.google.com/maps/search/?api=1&query=49.092412,18.643697" target="_blank" rel="noopener"
|
||||
class="text-sm font-semibold text-primary transition hover:underline dark:text-primary-dark">{{ t(key="page-stores-map-open", lang=L) }}</a>
|
||||
</div>
|
||||
<iframe title="{{ t(key='page-stores-map', lang=L) }}" loading="lazy" class="block h-72 w-full border-0 sm:h-96"
|
||||
referrerpolicy="no-referrer-when-downgrade"
|
||||
src="https://maps.google.com/maps?q=49.092412,18.643697&z=15&hl={{ L }}&output=embed"></iframe>
|
||||
</section>
|
||||
|
||||
{# Small facility photo next to the address. #}
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-stretch">
|
||||
<figure class="shrink-0 overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt sm:w-48">
|
||||
<img src="/static/img/store.jpg" alt="{{ t(key='home-stores-photo', lang=L) }}" width="142" height="115" loading="lazy"
|
||||
class="h-32 w-full object-cover sm:h-full" />
|
||||
</figure>
|
||||
<div class="flex-1 rounded-radius border border-outline bg-surface-alt p-4 dark:border-outline-dark dark:bg-surface-dark-alt">
|
||||
<div class="text-xs font-semibold uppercase tracking-wide text-on-surface/50 dark:text-on-surface-dark/50">{{ t(key="page-stores-address-label", lang=L) }}</div>
|
||||
<div class="mt-1 font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">{{ t(key="page-stores-facility", lang=L) }}</div>
|
||||
<address class="mt-1 not-italic text-on-surface/70 dark:text-on-surface-dark/70">{{ t(key="page-stores-address", lang=L) }}</address>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="rounded-radius border border-outline bg-surface-alt p-6 text-on-surface/70 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark/70">
|
||||
<p>{{ t(key="page-coming-soon", lang=L) }}</p>
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
for why — htmx hx-boost settles by id). #}
|
||||
<div x-data="{ isOpen: false, openedWithKeyboard: false }"
|
||||
x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false"
|
||||
class="relative">
|
||||
{{ ui::icon_button(aria_label=t(key='settings', lang=lang | default(value='sk')), attrs='x-on:click="isOpen = ! isOpen" x-on:keydown.space.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.down.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" aria-haspopup="true"', icon='<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>') }}
|
||||
class="relative self-stretch">
|
||||
{{ ui::icon_button(size="h-full w-9", aria_label=t(key='settings', lang=lang | default(value='sk')), attrs='x-on:click="isOpen = ! isOpen" x-on:keydown.space.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.down.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" aria-haspopup="true"', icon='<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /></svg>') }}
|
||||
<div x-cloak x-show="isOpen || openedWithKeyboard" x-transition x-trap="openedWithKeyboard"
|
||||
x-on:click.outside="isOpen = false, openedWithKeyboard = false"
|
||||
x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()"
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
that state (e.g. home) `view` is undefined, so the grid layout applies. #}
|
||||
<article
|
||||
class="group flex overflow-hidden rounded-radius border border-outline bg-surface-alt text-on-surface transition hover:border-primary dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:border-primary-dark"
|
||||
:class="view === 'list' ? 'flex-row flex-wrap' : 'flex-col'">
|
||||
:class="view === 'list' ? 'flex-col sm:flex-row' : 'flex-col'">
|
||||
<a href="/shop/{{ product.slug }}" class="flex min-w-0 flex-1"
|
||||
:class="view === 'list' ? 'flex-row' : 'flex-col'">
|
||||
<!-- Image -->
|
||||
<div class="relative overflow-hidden bg-surface-alt dark:bg-surface-dark"
|
||||
:class="view === 'list' ? 'size-28 shrink-0 sm:size-40' : 'h-44 md:h-64'">
|
||||
:class="view === 'list' ? 'w-28 shrink-0 self-stretch min-h-36 sm:w-48' : 'aspect-[5/4]'">
|
||||
{% if product.on_sale and product.percent_off > 0 %}
|
||||
<span class="absolute left-2 top-2 z-10 rounded-full bg-danger px-2 py-0.5 text-[11px] font-bold text-on-danger shadow-sm">−{{ product.percent_off }} %</span>
|
||||
{% endif %}
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
<!-- Content -->
|
||||
<div class="flex min-w-0 flex-1 flex-col gap-1"
|
||||
:class="view === 'list' ? 'p-4 sm:p-5' : 'p-6 pb-2'">
|
||||
:class="view === 'list' ? 'justify-center p-4 sm:p-5' : 'px-4 pt-3 pb-1'">
|
||||
<!-- Header: Title & Price (stacked so neither overflows the narrow card) -->
|
||||
<h3 class="break-words text-lg font-bold text-on-surface-strong dark:text-on-surface-dark-strong">{{ product.name }}</h3>
|
||||
{# Short blurb for the card; falls back to the full description (clamped)
|
||||
@@ -62,10 +62,10 @@
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex flex-col gap-2"
|
||||
:class="view === 'list' ? 'w-full justify-center p-4 sm:w-56 sm:p-5' : 'p-6 pt-0'">
|
||||
:class="view === 'list' ? 'w-full justify-center border-t border-outline p-4 sm:w-48 sm:self-stretch sm:border-l sm:border-t-0 sm:p-5 dark:border-outline-dark' : 'px-4 pb-4 pt-0'">
|
||||
{% if product.has_options %}
|
||||
{# Multiple variants: customer must pick on the product page. #}
|
||||
{{ ui::button(label=t(key="choose-option", lang=lang | default(value='sk')), href="/shop/" ~ product.slug, extra="w-full") }}
|
||||
{{ ui::button(label=t(key="choose-option", lang=lang | default(value='sk')), href="/shop/" ~ product.slug, extra="w-full", nowrap=false) }}
|
||||
{% elif product.in_stock %}
|
||||
<p class="text-xs text-on-surface/60 dark:text-on-surface-dark/60">{% if product.tracked %}{{ t(key="in-stock", lang=lang | default(value='sk')) }}: {{ product.stock }}{% else %}{{ t(key="available", lang=lang | default(value='sk')) }}{% endif %}</p>
|
||||
<form method="post" action="/cart/add" hx-post="/cart/add" hx-swap="none"
|
||||
@@ -73,7 +73,7 @@
|
||||
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="variant_id" value="{{ product.variant_id }}">
|
||||
<input type="hidden" name="quantity" value="1">
|
||||
{{ ui::button(label=t(key="add-to-cart", lang=lang | default(value='sk')), type="submit", extra="w-full", icon='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-3.5"><path fill-rule="evenodd" d="M5 4a3 3 0 0 1 6 0v1h.643a1.5 1.5 0 0 1 1.492 1.35l.7 7A1.5 1.5 0 0 1 12.342 15H3.657a1.5 1.5 0 0 1-1.492-1.65l.7-7A1.5 1.5 0 0 1 4.357 5H5V4Zm4.5 0v1h-3V4a1.5 1.5 0 0 1 3 0Zm-3 3.75a.75.75 0 0 0-1.5 0v1a3 3 0 1 0 6 0v-1a.75.75 0 0 0-1.5 0v1a1.5 1.5 0 1 1-3 0v-1Z" clip-rule="evenodd" /></svg>') }}
|
||||
{{ ui::button(label=t(key="add-to-cart", lang=lang | default(value='sk')), type="submit", extra="w-full", nowrap=false, icon='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-3.5 shrink-0"><path fill-rule="evenodd" d="M5 4a3 3 0 0 1 6 0v1h.643a1.5 1.5 0 0 1 1.492 1.35l.7 7A1.5 1.5 0 0 1 12.342 15H3.657a1.5 1.5 0 0 1-1.492-1.65l.7-7A1.5 1.5 0 0 1 4.357 5H5V4Zm4.5 0v1h-3V4a1.5 1.5 0 0 1 3 0Zm-3 3.75a.75.75 0 0 0-1.5 0v1a3 3 0 1 0 6 0v-1a.75.75 0 0 0-1.5 0v1a1.5 1.5 0 1 1-3 0v-1Z" clip-rule="evenodd" /></svg>') }}
|
||||
</form>
|
||||
{% else %}
|
||||
<p class="inline-flex justify-center rounded-radius bg-danger/10 px-3 py-2 text-xs font-medium text-danger">{{ t(key="out-of-stock", lang=lang | default(value='sk')) }}</p>
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
wrapper in _search.html (it persists across htmx swaps and is shared with the
|
||||
sort + view-toggle row); `_card.html` reads the same `view` to switch its own
|
||||
layout between a vertical card and a horizontal row. #}
|
||||
<div :class="view === 'list' ? 'flex flex-col gap-5' : 'grid grid-cols-2 gap-5 sm:grid-cols-3 xl:grid-cols-4'">
|
||||
{# Fixed-width cards (14rem) — same as the home page. Cards never stretch; the row
|
||||
just fits as many as the width allows. This keeps a card the exact same width on
|
||||
the shop and the home page regardless of how many columns fit. #}
|
||||
<div :class="view === 'list' ? 'flex flex-col gap-5' : 'grid grid-cols-2 gap-5 sm:grid-cols-[repeat(auto-fill,14rem)] sm:justify-center'">
|
||||
{% for product in products %}
|
||||
{% include "shop/_card.html" %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
Expects: query, selected_category, sort, plus the result vars consumed by
|
||||
_results.html. #}
|
||||
{% set L = lang | default(value='sk') %}
|
||||
<div x-data="{ view: localStorage.getItem('shopView') === 'list' ? 'list' : 'grid' }"
|
||||
<div x-data="{ view: localStorage.getItem('shopView') === 'grid' ? 'grid' : 'list' }"
|
||||
x-init="$watch('view', v => localStorage.setItem('shopView', v))"
|
||||
class="space-y-6">
|
||||
<form action="/search" method="get" role="search"
|
||||
@@ -42,7 +42,7 @@
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<button type="submit" class="shrink-0 rounded-radius bg-primary px-5 text-sm font-bold text-on-primary transition hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:bg-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark">
|
||||
<button type="submit" class="shrink-0 rounded-radius bg-cta px-5 text-sm font-bold text-on-cta transition hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cta dark:bg-cta-dark dark:text-on-cta-dark dark:focus-visible:outline-cta-dark">
|
||||
{{ t(key="search-button", lang=L) }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -62,7 +62,13 @@
|
||||
{{ ui::icon(name="search", size="size-3.5", extra="shrink-0") }}
|
||||
{{ t(key="search-scope-in", lang=L) }} <span class="font-semibold">{{ _scope }}</span>
|
||||
</span>
|
||||
{# This link descends from the search form, so it inherits its
|
||||
hx-target="#shop-results" / hx-swap="innerHTML". Switching scope is a
|
||||
real navigation (new breadcrumb, sidebar state, full-page response),
|
||||
so override the inherited target back to the body — otherwise the
|
||||
boosted full page gets nested inside the results region. #}
|
||||
<a href="/search{% if query %}?q={{ query | urlencode }}{% endif %}"
|
||||
hx-target="body" hx-swap="innerHTML"
|
||||
class="font-medium text-on-surface/60 underline-offset-2 hover:text-primary hover:underline dark:text-on-surface-dark/60 dark:hover:text-primary-dark">
|
||||
{{ t(key="search-scope-all", lang=L) }}
|
||||
</a>
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
{{ t(key="categories", lang=lang | default(value='sk')) }}
|
||||
</p>
|
||||
<div class="flex flex-col gap-1">
|
||||
{# mobile-only Home link: the navbar logo (the Home affordance) is hidden on
|
||||
small screens, so navigation home lives here in the drawer instead. #}
|
||||
<a href="/" data-nav="/" class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline aria-[current=page]:bg-primary/10 aria-[current=page]:text-on-surface-strong lg:hidden dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong dark:aria-[current=page]:bg-primary-dark/10 dark:aria-[current=page]:text-on-surface-dark-strong">
|
||||
{{ t(key="nav-home", lang=lang | default(value='sk')) }}
|
||||
</a>
|
||||
<a href="/shop" data-nav="/shop"
|
||||
class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline aria-[current=page]:bg-primary/10 aria-[current=page]:text-on-surface-strong dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong dark:aria-[current=page]:bg-primary-dark/10 dark:aria-[current=page]:text-on-surface-dark-strong">
|
||||
{{ t(key="all-products", lang=lang | default(value='sk')) }}
|
||||
@@ -71,7 +76,6 @@
|
||||
{% set L = lang | default(value='sk') %}
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<a href="/obchodne-podmienky" class="flex items-center gap-2 truncate rounded-radius px-2 py-1.5 text-sm text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong">{{ t(key="footer-terms", lang=L) }}</a>
|
||||
<a href="/o-nas" class="flex items-center gap-2 truncate rounded-radius px-2 py-1.5 text-sm text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong">{{ t(key="footer-about", lang=L) }}</a>
|
||||
<a href="/predajne" class="flex items-center gap-2 truncate rounded-radius px-2 py-1.5 text-sm text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong">{{ t(key="footer-stores", lang=L) }}</a>
|
||||
<a href="/doprava-a-platba" class="flex items-center gap-2 truncate rounded-radius px-2 py-1.5 text-sm text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong">{{ t(key="footer-shipping", lang=L) }}</a>
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
<!-- details -->
|
||||
{% set fld = "w-full rounded-radius border border-outline bg-surface-alt px-3 py-2 text-sm text-on-surface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark" %}
|
||||
{% set btn = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius px-5 py-2 text-sm text-center font-medium tracking-wide transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 border border-primary bg-primary text-on-primary focus-visible:outline-primary dark:border-primary-dark dark:bg-primary-dark dark:text-on-primary-dark dark:focus-visible:outline-primary-dark" %}
|
||||
{% set btn = "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-radius px-5 py-2 text-sm text-center font-medium tracking-wide transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 active:opacity-100 active:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-75 border border-cta bg-cta text-on-cta focus-visible:outline-cta dark:border-cta-dark dark:bg-cta-dark dark:text-on-cta-dark dark:focus-visible:outline-cta-dark" %}
|
||||
<script id="variant-data" type="application/json">{{ variants | json_encode() | safe }}</script>
|
||||
<div class="space-y-6" x-data="productBuy(JSON.parse(document.getElementById('variant-data').textContent))">
|
||||
{% if category %}
|
||||
|
||||
184
design-parity.md
184
design-parity.md
@@ -1,184 +0,0 @@
|
||||
# Design parity — Kompress mockup vs current storefront
|
||||
|
||||
Tracks the gap between the imported Claude Design mockup (`Kompress-eshop.html`,
|
||||
project `eshop` on claude.ai/design) and the live storefront. The home page body
|
||||
(featured grid + right rail) and the medical‑blue accent are **done**; this file
|
||||
lists everything else from the mockup that is still missing or only half‑wired, so
|
||||
it can be picked up incrementally.
|
||||
|
||||
Constraints for all items: **htmx + Alpine + PenguinUI + Tailwind tokens only.**
|
||||
Express colors with the existing design tokens (`bg-primary`, `bg-surface-alt`,
|
||||
`border-outline`, `dark:*` …) — never inline hex — so light **and** dark both work.
|
||||
PenguinUI is copy‑paste: lift markup out of `penguinui-components/<name>/…`
|
||||
(read‑only, never `{% include %}`'d) and adapt at the use‑site.
|
||||
|
||||
Legend: 🟢 backend already exists, only UI missing · 🟡 partial · 🔴 net‑new.
|
||||
|
||||
---
|
||||
|
||||
## A. Global chrome (`assets/views/base.html`)
|
||||
|
||||
These affect every page, so they live in `base.html`, not the home template.
|
||||
|
||||
> **✅ Implemented (pass 2)** — A1–A6 are now built in `base.html`, with i18n keys
|
||||
> added to `assets/i18n/{sk,en}/main.ftl` (`top-contact`, `search-button`,
|
||||
> `welcome`, `brand-subtitle`, `footer-*`, `hotline`, …). Verified: `/`, `/shop`,
|
||||
> `/login`, `/register`, `/cart`, `/search?q=` all 200, no render errors.
|
||||
> The `Kontakt` / `Mapa stránky` top-bar links and the footer info links point to
|
||||
> `#` until real pages exist.
|
||||
|
||||
### A1. Top utility bar — ✅ done
|
||||
Mockup: thin white bar above the header, right‑aligned links *Kontakt* · *Mapa
|
||||
stránky*.
|
||||
- New `<div>` strip at the very top of `base.html`, `border-b border-outline
|
||||
bg-surface text-xs text-on-surface/70`.
|
||||
- Links to a future `/kontakt` and `/sitemap` (or existing pages).
|
||||
- Low effort, no backend.
|
||||
|
||||
### A2. In‑header search bar — ✅ done
|
||||
Mockup: full‑width search input with a magnifier icon + blue **Hľadať** submit
|
||||
button, centered in the header.
|
||||
- **Backend already exists**: `GET /search` (`src/controllers/shop.rs:353`,
|
||||
`run_search` at `:90`, fragment `assets/views/shop/_search.html`). Just needs a
|
||||
header form: `<form action="/search" method="get">` with `name="q"`, optionally
|
||||
`hx-get="/search" hx-trigger="keyup changed delay:300ms"` for live results.
|
||||
- Copy markup from `penguinui-components/navbar/with-search.html` and/or
|
||||
`penguinui-components/text-input/search-input.html`.
|
||||
- Medium effort — mostly wiring the existing endpoint into the header layout.
|
||||
|
||||
### A3. Logo lockup with subtitle — ✅ done
|
||||
Mockup: rounded blue tile with a "+" cross glyph + **KOMPRESS** + uppercase
|
||||
subtitle *zdravotnícke potreby*.
|
||||
- Current logo is a plain text wordmark in `base.html`. Add the icon tile
|
||||
(`bg-primary text-on-primary`, `rounded-radius`) and the muted subtitle line.
|
||||
- Cross SVG is in the mockup (two rounded `<rect>`s). Low effort.
|
||||
|
||||
### A4. Account button (two‑line) — ✅ done
|
||||
Mockup: person icon + stacked *Vitajte* / *Prihláste sa* (or the customer name when
|
||||
logged in).
|
||||
- Logged‑in/out state already available via `logged_in_customer` /
|
||||
`customer_name` and the existing `partials/profile_menu.html`.
|
||||
- Restyle the login link / profile menu trigger into the two‑line button. Low effort.
|
||||
|
||||
### A5. Cart button with text label — ✅ done
|
||||
Mockup: cart icon + red count badge + stacked *Košík* / *N produktov* label.
|
||||
- The icon + cookie‑driven badge already exist in `base.html` (`cartCount()` JS).
|
||||
Add the text label beside it; pluralize "produkt/produkty/produktov" (mirror the
|
||||
mockup's `plural()` helper, or add an i18n key).
|
||||
- Low effort.
|
||||
|
||||
### A6. Footer — ✅ done
|
||||
Mockup: 4‑column footer (brand blurb · Informácie · Účet · Kontakt) + copyright bar.
|
||||
- **No footer exists anywhere** (`grep "<footer"` → none). Add once to `base.html`
|
||||
after `<main>`.
|
||||
- Static links; reuse the same i18n keys as the nav. Low effort, high visual payoff.
|
||||
|
||||
---
|
||||
|
||||
## B. Listing layout (`shop/category.html`, `shop/_product_grid.html`)
|
||||
|
||||
The mockup's body is really a **category/listing** layout. The home page borrows its
|
||||
look; the real listing pages should adopt the rest.
|
||||
|
||||
> **✅ Implemented (pass 3)** — reusable `ui::crumb` / `ui::crumb_current` chevron
|
||||
> breadcrumb macros (`assets/views/macros/ui.html`), wired into `home/index.html`
|
||||
> (Domov), `shop/index.html` (Domov › Obchod) and restyled on `shop/category.html`
|
||||
> (Domov › Obchod › … › category). Added the sidebar **Informácie** card to
|
||||
> `shop/_sidebar.html`. While verifying, confirmed **B3 sort**, **B4 view toggle**
|
||||
> and **C1 collapsible tree** were already built — only styling polish remains.
|
||||
> Verified: `/`, `/shop`, `/partials/categories`, `/search?q=` all 200, no errors.
|
||||
|
||||
### B1. Breadcrumbs — ✅ done
|
||||
Mockup: *Domov › Zdravotnícke › {kategória}* with chevron separators.
|
||||
- **Already implemented** on category pages: `breadcrumbs` loop in
|
||||
`assets/views/shop/category.html:12`. Just needs the mockup's chevron styling and
|
||||
to be surfaced on more pages (e.g. product detail, optionally a "Domov" crumb on
|
||||
home).
|
||||
- Copy from `penguinui-components/breadcrumbs/breadcrumb-with-chevron.html`.
|
||||
- Low effort (restyle existing).
|
||||
|
||||
### B2. Category banner card — 🔴
|
||||
Mockup: white card with a 148px image placeholder + category description paragraph +
|
||||
**Viac ›** link, shown above the product list.
|
||||
- Per‑category copy/image. Needs a `description` (and optional banner image) field on
|
||||
the category model, or static copy keyed by slug for now.
|
||||
- Render as the app card idiom (`border border-outline bg-surface-alt dark:…`).
|
||||
- Medium effort (model field) or low (static placeholder first).
|
||||
|
||||
### B3. Sort control ("Zoradiť podľa") — ✅ already in toolbar
|
||||
Mockup: styled `<select>` — Predvolené / Cena ↑ / Cena ↓ / Názov A–Z /
|
||||
Najpredávanejšie.
|
||||
- **Backend already exists**: `sort` query param (`src/controllers/shop.rs:41,81,
|
||||
179‑181`). Needs the styled select wired to it (`hx-get` on `change`, preserving
|
||||
the query + filters via the existing `build_query` helper at `shop.rs:63`).
|
||||
- Copy from `penguinui-components/select/default-select.html`.
|
||||
- Low effort.
|
||||
|
||||
### B4. View grid/list toggle — 🟢 (done elsewhere)
|
||||
- **Already implemented** in `assets/views/shop/_product_grid.html` (Alpine `view`
|
||||
state, persisted to `localStorage`). The mockup's toggle is the same idea; just
|
||||
align the icon/segmented‑button styling with the mockup. Cosmetic only.
|
||||
|
||||
### B5. "Porovnať" (compare) — 🔴
|
||||
Mockup: a *Porovnať* button in the listing toolbar.
|
||||
- **No compare feature exists.** Net‑new: select‑to‑compare state (Alpine + a cookie
|
||||
or server list) and a compare view. Largest item here — defer unless wanted.
|
||||
|
||||
### B6. List‑view product card — ✅ done
|
||||
Mockup list rows are richer (left thumbnail, description, stock pill, price block,
|
||||
add/options/out‑of‑stock button on the right).
|
||||
- `shop/_card.html` already has a `view === 'list'` horizontal layout. Close already;
|
||||
optionally add the stock pill (*Skladom* / *Vypredané*) and discount badge to match.
|
||||
- Low effort.
|
||||
|
||||
---
|
||||
|
||||
## C. Left sidebar (`assets/views/partials/categories.html` via `/partials/categories`)
|
||||
|
||||
Mockup left column = two white cards: a collapsible **Kategórie** tree and an
|
||||
**Informácie** links card. base.html already renders `#category-sidebar` from
|
||||
`/partials/categories`, so this is a restyle of that partial.
|
||||
|
||||
### C1. Collapsible category tree — ✅ already implemented
|
||||
Mockup: top‑level groups with +/− chevrons; expanded group shows leaf links, active
|
||||
leaf highlighted (`bg-primary/10 text-primary font-bold`).
|
||||
- Style the existing categories partial as a white card with Alpine‑toggled
|
||||
sub‑lists (`x-data="{ open: … }"`). Active state via `aria-current` + the existing
|
||||
`markActiveNav()` pattern.
|
||||
- Medium effort.
|
||||
|
||||
### C2. "Informácie" links card — ✅ done
|
||||
Mockup: second sidebar card with Obchodné podmienky / O našej spoločnosti / Naše
|
||||
obchody / Doprava a platba.
|
||||
- Static links card; same markup as the contact/stores cards already added to the
|
||||
home right rail. Low effort.
|
||||
|
||||
---
|
||||
|
||||
## D. Right rail — mostly done
|
||||
|
||||
Implemented on the home page: **Najpredávanejšie** (from featured products), **Naše
|
||||
obchody** placeholder, **Kontaktujte nás** blue card.
|
||||
|
||||
Remaining 🟡:
|
||||
- **D1. Bestsellers from real sales data** — currently reuses the featured query;
|
||||
wire to actual order/sales counts when available.
|
||||
- **D2. "Naše obchody" real photo + page** — replace the placeholder block and link
|
||||
to a real stores page.
|
||||
- **D3. Contact hotline** — phone number is hardcoded; move to config/i18n.
|
||||
|
||||
---
|
||||
|
||||
## Suggested order (quick wins first)
|
||||
1. Footer (A6) + top utility bar (A1) — pure markup, big visual lift.
|
||||
2. Header search (A2) + cart label (A5) + logo lockup (A3) — wire existing data.
|
||||
3. Sort select (B3) + breadcrumb restyle (B1) — backends already exist.
|
||||
4. Sidebar restyle (C1, C2).
|
||||
5. Category banner (B2), richer list card (B6).
|
||||
6. Compare (B5) — only if the feature is actually wanted.
|
||||
|
||||
## Reference
|
||||
- Mockup source extracted to (scratch, regenerate via DesignSync if needed):
|
||||
`DesignSync get_file` on project `015b8bf5-fd27-4a7e-82d9-d1864a59578c`,
|
||||
file `Kompress-eshop.html` → `__bundler/template` script holds the real markup.
|
||||
- PenguinUI workflow: see `penguinui-workflow` memory + `hardcoded-inventory.md`.
|
||||
123
docs/real-site-data-to-port.md
Normal file
123
docs/real-site-data-to-port.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Real data to port from www.e-shop.kompress.sk
|
||||
|
||||
Source: <http://e-shop.kompress.sk/> (live PrestaShop site). This file lists the
|
||||
real business content that exists on the production site but is **not** yet in
|
||||
this app, so it can be ported into our catalog / CMS / config.
|
||||
|
||||
> Status note: the header branding has already been switched from the
|
||||
> placeholder "Kompress eshop" to the real logo (`assets/static/img/logo.jpg`,
|
||||
> the blue **KOMPRESS** wordmark pulled from `/img/logo.jpg` on the live site)
|
||||
> and the `brand` / `meta-description` i18n keys now carry the real company
|
||||
> name and tagline.
|
||||
|
||||
---
|
||||
|
||||
## 1. Company / branding
|
||||
|
||||
| Field | Real value |
|
||||
|---|---|
|
||||
| Legal name | **WWW.KOMPRESS.SK, s.r.o.** |
|
||||
| Display name | www.e-shop.kompress.sk |
|
||||
| Tagline (meta) | *Výrobca a distribútor zdravotníckych pomôcok a potrieb* (Manufacturer and distributor of medical aids and supplies) |
|
||||
| Logo | blue **KOMPRESS** wordmark — `/img/logo.jpg` (260×52), saved to `assets/static/img/logo.jpg` |
|
||||
| Keywords | obchod, výroba, distribúcia, striekačky, ihly, krytie, sušenie, jednorázový, materiál, štvorce |
|
||||
|
||||
## 2. Contact / legal (for footer + invoicing config)
|
||||
|
||||
- **Sídlo (registered seat):** Gunduličova 4, 811 05 Bratislava
|
||||
*(the homepage footer block also shows "Moyzesova 3, 811 05 Bratislava" — confirm which is current)*
|
||||
- **Prevádzka (operations / warehouse):** Nádražná 328/62, 015 01 Rajec nad Rajčankou
|
||||
- **Registrácia:** Obchodný register Okresného súdu v Bratislave, Odd. Sro, Vložka číslo: 102522/B
|
||||
- **Telefón / hotline:** +421 903 410476
|
||||
- **E-mail:** kompress@kompress.sk
|
||||
|
||||
## 3. "O našej spoločnosti" (About) — CMS page
|
||||
|
||||
> WWW.KOMPRESS.SK, s.r.o., Sídlo: Gunduličova 4, 811 05 Bratislava, Prevádzka:
|
||||
> Nádražná 328/62, 015 01 Rajec nad Rajčankou, Registrácia: Obchodný register
|
||||
> Okresného súdu v Bratislave Odd. Sro, Vložka číslo: 102522/B. Spoločnosť
|
||||
> dodáva zdravotnícke potreby a svojich zákazníkov tradične oslovuje vysokou
|
||||
> kvalitou a nízkou cenou. Samozrejmosťou je doprava tovaru až k rukám
|
||||
> odberateľa a kvalitný zákaznícky servis.
|
||||
|
||||
Other CMS pages present on the live site:
|
||||
- `O našej spoločnosti` — `/content/4-kompress`
|
||||
- `Podmienky používania obchodu` (terms) — `/content/3-podmienky-pouzivania-obchodu`
|
||||
|
||||
## 4. Product categories (PrestaShop IDs → name / description)
|
||||
|
||||
These are the real catalog categories. Top-level is **Zdravotnícke pomôcky**
|
||||
(id 7); the rest are its subtree. The text below is the real category
|
||||
description copy to port.
|
||||
|
||||
| ID | Category / description copy |
|
||||
|---|---|
|
||||
| 7 | **Zdravotnícke pomôcky** (root) |
|
||||
| 8 | **Gáza, role, štvorce, prírezy** — Gáza a výrobky z gázy, prírezy, role, zložky, kompresy resp. štvorce, pásy resp. longety, sterilné či nesterilné. Čistá biela bavlna. |
|
||||
| 9 | **Vata** (buničitá, obväzová, stomatologická) — bielená, v rezoch, v návine, delená na tampóny, skladaná ako harmonika, savá a mäkká. |
|
||||
| 10 | **Netkané textílie** — Pervin, SMS obaly na sterilizáciu, návleky na obuv. |
|
||||
| 11 | **Textil, Sanavel** |
|
||||
| 12 | **Plasty, injekčná technika** — vrecká, tŕne, hadičky, ihly, striekačky, infúzne súpravy, urínové vrecká, odberové vaky, cievky, katétre Nelaton, odsávačky. |
|
||||
| 13 | **Papier na lôžko** — krepovaný biely/farebný, tissue embosovaný, laminovaný tissue s plastovou spodnou vrstvou. |
|
||||
| 14 | **Somatické / psycho-somatické prístroje** — pomôcky na elimináciu geopatogénnych zón, elektrosmogu. |
|
||||
| 15 | **Somavedic** (rad prístrojov) — eliminácia vplyvu geopatogénnych zón, elektrosmogu; dosah 30 m. |
|
||||
| 16 | **Úprava vody** — voda ako nosič energie. |
|
||||
| 17 | **Gely a lubrikanty** — gély na sonografiu/ECG, lubrikanty na sondy; balenia 260/500/1000 ml, kanister 5 l. |
|
||||
| 18 | **Somavedic AURUM** — najúčinnejší z kolekcie. |
|
||||
| 19 | **Tecasorb** — moderné absorpčné krytie (vlhké aj suché). |
|
||||
| 20 | (Somavedic — biofyzikálne sledovanie IEGF) |
|
||||
| 21 | **Obväzy** — fixačné, elastické ovínadlá, sádrové obväzy, gumové škrtidlá MARTIN, ESMARCH. |
|
||||
| 22 | **Nástroje** — čepelky, peany, nožničky, pinzety. |
|
||||
| 23 | **Pre invalidov** — vozíky, postele, matrace, návleky, poťahy, stoličky, nádstavce na WC, antidekubitné matrace. |
|
||||
| 24 | **Proti preležaninám** — antidekubitné matrace, poťahy, podložky, sedáky, motorové pumpy. |
|
||||
| 25 | **Barle, palice, chodúliky** — chodítka, statické/pohyblivé chodúliky s kolieskami. |
|
||||
| 26 | **Vozíky pre invalidov - mechanické** — bez pohonu, polohovateľné, skladacie. |
|
||||
| 27 | **Držadlá, nástavce na WC, operadlá do kúpeľne** — namontovateľné na stenu/umývadlo/WC. |
|
||||
| 28 | **Ochrana matracov** — umývateľné poťahy, návleky, obliečky. |
|
||||
| 29 | **Ortopedické pomôcky** — vložky do topánok, silikónové, medziprstové, gelové. |
|
||||
| 30 | **Polohovacie pomôcky** — podložky s výrezom, sedáky, krúžky, valce. |
|
||||
| 31 | **Do kúpeľne** — stolička/sedačka/opierka/držadlo do vane a sprchy, protišmykové, nastaviteľné. |
|
||||
| 32 | **Relaxácia a rehabilitácia** — lopty, balóny, gumové pásy, pedále, masážne loptičky. |
|
||||
| 33 | **Sebaobsluha a obsluha pacienta** — obúvanie, poháre, pásy na dvíhanie, sklápacie stolíky. |
|
||||
| 34 | **Toaletné kreslá a toaletné vozíky** — kreslá/vozíky s otvorom v sedadle, kreslo do sprchy. |
|
||||
| 35 | **Somavedic MEDIC** — certifikát IGEF na rušenie elektrosmogu. |
|
||||
| 36 | **Rukavice** — vyšetrovacie, chirurgické bezpúdrové/púdrované, latexové, nitrilové, vinylové, neoprénové. |
|
||||
|
||||
## 5. Featured products (homepage) with prices
|
||||
|
||||
Prices are EUR (display on site uses `X,XX €`). Image paths are on the live
|
||||
site under `/<id>-home_default/<slug>.jpg`.
|
||||
|
||||
| Product | Price (€) | Notes |
|
||||
|---|---|---|
|
||||
| Somavedic URAN | 600,00 | id 581 |
|
||||
| Somavedic MEDIC (elektrosmog eliminátor) | 360,00 | id 213 |
|
||||
| Somavedic ATLANTIK | 300,00 | id 572 |
|
||||
| Sedačka do sprchy nastaviteľná, s opierkou chrbta a výrezom | 65,00 | id 497 |
|
||||
| Opierka pod chrbát polohovateľná | 37,20 | id 378 |
|
||||
| Krepovaný papier na lôžko (papier na operačné stoly) | 4,80 | id 248 |
|
||||
| GAMMEX chirurgické rukavice – pár | 1,23 | id 582 |
|
||||
| Gáza v páse s buničitou vatou (Mullro) | 1,14 / 1,23 | id 567 |
|
||||
| Návleky na topánky NT | 0,12 | id 224 |
|
||||
| Návlek na obuv (plastový s gumičkou) | 0,04 | id 279 |
|
||||
| Gázové kompresy, štvorce nesterilné | — | id 100 |
|
||||
|
||||
Other Somavedic models referenced on site: **Somavedic AURUM** (id 18 cat).
|
||||
|
||||
## 6. Storefront blocks present on live site (UX reference)
|
||||
|
||||
- Home slider (homeslider) — "pomôcky pre pacientov", "prístroj Somavedic aurum".
|
||||
- "Naše obchody" (Our stores / blockstore) block.
|
||||
- Reinsurance block (5 trust badges) — `blockreinsurance`.
|
||||
- Price-comparison badges: Pricemania, Heureka.sk, Tovar.sk, NajNakup.sk.
|
||||
|
||||
## 7. Suggested port order
|
||||
|
||||
1. **Config/contact** — drop real legal name, seat, ops address, IČO/registration,
|
||||
phone, email into footer + invoicing config (see `account-type-rules` memory).
|
||||
2. **CMS pages** — seed `O našej spoločnosti` and `Obchodné podmienky` content.
|
||||
3. **Categories** — seed categories 7–36 (names + descriptions above) under root
|
||||
"Zdravotnícke pomôcky"; map to our category model.
|
||||
4. **Products** — import the featured products with prices/variants; pull images
|
||||
from `/<id>-home_default/` on the live site.
|
||||
5. **Currency** — site prices are EUR (matches our EUR base; CZK display optional).
|
||||
Reference in New Issue
Block a user