Files
kompress_eshop/hardcoded-inventory.md
2026-06-17 21:24:45 +02:00

481 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Handcoded UI Components — Penguin UI Replacement Index
> **Scope**: Every handcoded UI component.
> Each item maps to a [Penguin UI](https://github.com/SalarHoushvand/penguinui-components/tree/main) component that duplicates the same purpose with fewer lines and better accessibility.
## Vendoring convention
When a Penguin UI component can replace a handcoded one, we vendor its source
and then use it (instead of hand-rolling):
1. Copy the component's source **byte-for-byte** from the [Penguin UI repo](https://github.com/SalarHoushvand/penguinui-components/tree/main)
into `assets/views/penguinui/`, **mirroring the upstream repo hierarchy**
(e.g. `toast-notification/stacking-toast-notification.html`). This directory
is reserved exclusively for vendored Penguin UI components and is kept an
**exact, unmodified mirror** of upstream — demo triggers, bugs and all. It's
a reference, not the rendered markup.
2. Adapt it where it's actually used (strip docs-only demo triggers, fix obvious
upstream bugs, wire data bindings). Note the deviations in a comment next to
the adapted copy.
3. Rebuild Tailwind (`make css`) so any new utility classes get compiled.
4. Mark the section below as ✅ **DONE**.
---
## 0. Toast — ✅ DONE
**Penguin UI: `toast-notification/stacking-toast-notification.html`**
- Exact upstream mirror at `assets/views/penguinui/toast-notification/stacking-toast-notification.html` (reference only)
- Adapted/rendered copy lives inline in `assets/views/base.html` (demo triggers
removed; the upstream dismiss-button `<svg>` quote bugs fixed)
- The global `toast('message')` JS helper now dispatches the component's
`notify` event (`{ variant: 'success', message }`), so existing callsites
(`shop/show.html`, `shop/_card.html`) keep working unchanged.
---
## 1. Navbar
**Penguin UI: `navbar/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 1 | `assets/views/base.html:63-191` | Full site navbar: brand, desktop nav links, cart icon+badge, settings dropdown, mobile hamburger → mobile panel | ~130 lines |
| 2 | `assets/views/admin/base.html:102-114` | Admin top bar: hamburger toggle + breadcrumb text | ~13 lines |
**Details for #1 (site navbar):**
- **Brand/logo**: `base.html:74-77` — plain `<a>` with text
- **Desktop nav links**: `base.html:80-92``<ul>` with 45 items, manual `aria-current` routing
- **Cart icon + badge**: `base.html:96-109` — hand-rolled SVG cart icon + an Alpine `x-data` badge that reads `document.cookie` directly
- **Settings dropdown**: `base.html:110-162` — gear-icon trigger + language-switcher `<form>` + theme tristate (system/light/dark)
- **Mobile hamburger**: `base.html:164-172` — hamburger SVG button
- **Mobile menu panel**: `base.html:175-190` — dropdown `<ul>` with duplicated nav links
**Penguin navbar variants:** `default-navbar.html`, `with-call-to-action.html`, `with-search.html`, `with-user-profile.html`
---
## 2. Sidebar (Admin) — ✅ DONE
**Penguin UI: `sidebar/simple-sidebar.html`**
- Exact upstream mirror at `assets/views/penguinui/sidebar/simple-sidebar.html` (reference only)
- Adapted at use-site in `assets/views/admin/base.html`: the nav links + bottom
exit/logout now use Penguin's link treatment (`hover:bg-primary/5`,
`underline-offset-2 focus-visible:underline focus:outline-hidden`) and the
subtle active state (`bg-primary/10` + `text-on-surface-strong`) mapped onto
our `data-nav`/`aria-current` so `markActiveNav()` still drives it.
- The fixed-rail translate-X show/hide mechanics + mobile overlay (#4) are layout
scaffolding, kept as-is. Icons were intentionally not added (no verified icon
set yet) — possible follow-up.
---
## 3. Sidebar (Category Accordion) — ✅ DONE
**Penguin UI: `sidebar/sidebar-with-collapsible-menus.html`**
- Exact upstream mirror at `assets/views/penguinui/sidebar/sidebar-with-collapsible-menus.html` (reference only)
- Adapted at use-site in `assets/views/shop/_sidebar.html`: Penguin link treatment +
active state + chevron-down rotation (`rotate-180`); child items now sit in a
bordered/indented list instead of the old `padding-left:28px` + `↳`. Kept our
htmx partial, data-driven `category_groups`, auto-expand `x-init`, and
`data-nav`/`markActiveNav()` active routing.
- Deviations: group row keeps our link + chevron-toggle split (categories are
navigable, not just expandable); uses `x-show`/`x-transition` instead of
upstream's `x-collapse` (that Alpine plugin isn't bundled in our build).
- The `<aside>` drawer + mobile overlay (#6) in `base.html` are layout
scaffolding, kept as-is.
---
## 4. Dropdown (Settings)
**Penguin UI: `dropdown-menu/` (dropdown-with-click.html, dropdown-with-icons.html)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 7 | `assets/views/base.html:110-162` | Language + theme settings dropdown (gear icon trigger) | ~53 lines |
| 8 | `assets/views/admin/base.html:117-166` | **100% duplicate** of the same dropdown | ~50 lines |
**Details:**
- Both dropdowns are **identical** (copy-paste), totaling ~103 lines of duplicated code
- Alpine `x-data="{ open: false }"` + `@click.outside`
- Language switcher: English / Slovenčina buttons in a `<form method="post" action="/lang">`
- Theme tristate: system / light / dark with `setTheme()` and `currentTheme()` from inline JS
- Gear cog inline SVG icon
---
## 5. Country / Phone Combobox
**Penguin UI: `text-input/` + custom dropdown list**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 9 | `assets/views/shop/checkout.html:49-74` | Phone prefix combobox (`+421`, `+420`, …, `+33`) | ~25 lines |
| 10 | `assets/views/shop/checkout.html:102-127` | Country combobox (SK, CZ, AT, DE, PL, HU) | ~26 lines |
**Details for #9:**
- Alpine `x-data` with `prefix`, `prefixOpen`, `opts` array of `{ v, l }` (9 country codes)
- Manual `filtered` computed property
- Inline chevron SVG that rotates via `:class="prefixOpen && 'rotate-180'"`
- Dropdown list with `<template x-for>` and `@click` selection
**Details for #10:**
- Same pattern as #9 but with translate-able country names (6 countries)
- Includes `+421` prefix shortcut
---
## 6. Product Card — ✅ DONE
**Penguin UI: `card/ecommerce-product-card.html`**
- Exact upstream mirror at `assets/views/penguinui/card/ecommerce-product-card.html` (reference only)
- Adapted/rendered copy is `assets/views/shop/_card.html`: `<article>` shell + Penguin
image/title/price layout and the cart-icon add-to-cart button, wired to our product
data + i18n + htmx `hx-post` add-to-cart + `toast()`. Demo-only rating stars,
hardcoded content and `max-w-sm` (fights the shop grid) were dropped; whole card
links to the product page; out-of-stock badge kept.
---
## 7. Product Image Gallery
**Penguin UI: `carousel/` (3 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 12 | `assets/views/shop/show.html:8-26` | Image gallery with main image + thumbnail strip, Alpine `x-data="{ active: 0 }"` | ~19 lines |
**Details:**
- Main image: `x-show="active === {{ loop.index0 }}"` with `object-cover`
- Thumbnail buttons: border changes to indicate active state
- No transition/animation between images — just x-show toggling
---
## 8. Radio-Button Groups
**Penguin UI: radio (part of form inputs)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 13 | `assets/views/shop/checkout.html:133-165` | Carrier selection radio group (each option shows name + price) | ~33 lines |
| 14 | `assets/views/shop/checkout.html:167-180` | Payment method radio group (COD + bank transfer) | ~14 lines |
**Details for #13:**
- `{% for m in shipping_methods %}` loop
- Each `<label>` is a styled card with `has-[:checked]:border-primary` border highlight
- Radio input triggers `@change` to update Alpine state (carrier, carrierPrice, requiresPoint)
- Pickup-point sub-panel shown via `x-show="requiresPoint"`
**Details for #14:**
- Two hardcoded radio options: COD and bank_transfer
- `x-model="paymentMethod"` binding
---
## 9. Checkbox
**Penguin UI: `checkbox/` (3 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 15 | `assets/views/admin/catalog/product_form.html:85-89` | "Published" checkbox | ~5 lines |
| 16 | `assets/views/admin/catalog/category_form.html:67-71` | "Published" checkbox | ~5 lines |
| 17 | `assets/views/admin/shipping/index.html:25-29` | "Enabled" checkbox | ~5 lines |
---
## 10. Text Input
**Penguin UI: `text-input/` (8 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 18 | `assets/views/shop/checkout.html:37-44` | Email + name text inputs | ~8 lines |
| 19 | `assets/views/shop/checkout.html:84-99` | Address, city, ZIP text inputs | ~16 lines |
| 20 | `assets/views/admin/login.html:34-51` | Email + password inputs (with focus ring styles) | ~18 lines |
| 21 | `assets/views/admin/catalog/product_form.html:19-68` | Name, price, currency, stock, SKU, slug inputs + textarea | ~50 lines |
| 22 | `assets/views/admin/catalog/category_form.html:19-55` | Name, slug, position inputs + textarea | ~37 lines |
| 23 | `assets/views/shop/show.html:46-48` | Quantity number input | ~3 lines |
| 24 | `assets/views/shop/_cart_body.html:30-38` | Quantity number input with `@change` confirmation dialog | ~9 lines |
| 25 | `assets/views/admin/shipping/index.html:20-24` | Price text input | ~5 lines |
**Pattern: Every input is hand-styled with:**
```
w-full rounded-radius border border-outline bg-surface px-3 py-2 text-sm text-on-surface
focus:outline-2 focus:outline-primary
dark:border-outline-dark dark:bg-surface-dark dark:text-on-surface-dark
```
This exact class string appears 15+ times across the codebase.
---
## 11. Textarea
**Penguin UI: `textarea/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 26 | `assets/views/shop/checkout.html:183-186` | Order note textarea | ~4 lines |
| 27 | `assets/views/admin/catalog/product_form.html:71-73` | Product description textarea | ~3 lines |
| 28 | `assets/views/admin/catalog/category_form.html:53-55` | Category description textarea | ~3 lines |
---
## 12. Select/Dropdown (Native)
**Penguin UI: `select/` (7 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 29 | `assets/views/admin/catalog/product_form.html:53-60` | Category select | ~8 lines |
| 30 | `assets/views/admin/catalog/category_form.html:40-49` | Parent category select (tree indented with `—&nbsp;`) | ~10 lines |
| 31 | `assets/views/admin/orders/show.html:108-112` | Order status select | ~5 lines |
---
## 13. File Input
**Penguin UI: `file-input/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 32 | `assets/views/admin/catalog/product_form.html:78-82` | Product image upload | ~5 lines |
| 33 | `assets/views/admin/catalog/category_form.html:58-64` | Category image upload | ~7 lines |
**Both use the same Tailwind `file:mr-3 file:...` prefix pattern for styling.**
---
## 14. Table
**Penguin UI: `table/` (7 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 34 | `assets/views/admin/orders/index.html:11-36` | Orders table: number, customer, status pill, total, "View" link | ~26 lines |
| 35 | `assets/views/admin/orders/show.html:20-44` | Order items table: product, quantity, line total + tfoot summary | ~25 lines |
| 36 | `assets/views/admin/catalog/products.html:20-70` | Products table: image+name+category, price, stock, status pill, edit/view/delete actions | ~51 lines |
| 37 | `assets/views/admin/catalog/categories.html:20-59` | Categories table: tree-indented name, product count, status pill, edit/delete | ~40 lines |
| 38 | `assets/views/shop/_cart_body.html:6-59` | Cart table: product link, price, quantity input, line total, remove button + tfoot total | ~54 lines |
**Pattern:** Every table uses the same class structure:
```
<table class="w-full text-left text-sm">
<thead class="border-b border-outline bg-surface-alt text-xs uppercase tracking-wide text-on-surface/70">
<tbody class="divide-y divide-outline">
<tr class="hover:bg-surface-alt">
```
This is copy-pasted 5 times.
---
## 15. Alert / Error Banner
**Penguin UI: `alert/` (6 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 39 | `assets/views/admin/login.html:25-30` | Login error alert (`role="alert"`, danger border) | ~6 lines |
| 40 | `assets/views/admin/orders/show.html:13-15` | Ship error alert (danger border) | ~3 lines |
**Pattern:** `rounded-radius border border-danger/40 bg-danger/10 px-3 py-2 text-sm text-danger`
---
## 16. Badge / Status Pill
**Penguin UI: `badge/` (7 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 41 | `assets/views/base.html:107-108` | Cart item-count badge (absolute-positioned, number) | ~2 lines |
| 42 | `assets/views/admin/login.html:14-16` | "Auth" badge on login card header | ~3 lines |
| 43 | `assets/views/admin/orders/index.html:27` | Order status inline pill | ~1 line |
| 44 | `assets/views/admin/catalog/products.html:49-53` | Published/Draft status pill | ~5 lines |
| 45 | `assets/views/admin/catalog/categories.html:40-44` | Published/Draft status pill | ~5 lines |
| 46 | `assets/views/shop/_card.html:27` | "Out of stock" badge (danger background) | ~1 line |
---
## 17. Buttons
**Penguin UI: `buttons/` (6 variants)**
| # | Location | What it is | Style | Count |
|---|----------|------------|-------|-------|
| 47 | Across all templates | Primary button | `bg-primary text-on-primary rounded-radius px-4 py-2` | 15+ |
| 48 | Across all templates | Outline/secondary button | `border border-outline text-on-surface rounded-radius px-3 py-1.5` | 20+ |
| 49 | `base.html:86-88`, `admin/base.html:92-95` | Danger button (logout, delete) | `text-danger` with hover bg | 4 |
| 50 | `base.html:84` | Warning-colored button (admin link) | `text-warning` | 1 |
| 51 | `base.html:112-113`, `admin/base.html:118-119`, ... | Icon-only ghost button (gear, hamburger, chevron) | `size-9 inline-flex items-center justify-center` | 10+ |
| 52 | `_cart_body.html:46` | Text/link button (Remove) | `text-danger hover:underline` | 1 |
Summary of button variants handcoded:
- `bg-primary` (solid primary)
- `border border-outline` (outline)
- `file:bg-primary` (file input button)
- `text-danger` / `text-warning` (semantic)
- `hover:opacity-75` vs `hover:opacity-90` — inconsistent hover effects
- `tracking-wide` on some, not others
---
## 18. Toggle / Switch
**Penguin UI: `toggle/` (2 variants)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 53 | `assets/views/base.html:13-30` | Theme toggle (dark/light/system) — inline `<script>` JavaScript | ~18 lines |
| 54 | `assets/views/admin/base.html:13-30` | **Exact duplicate** of the theme toggle JS | ~18 lines |
**Details:**
- `applyTheme()`, `setTheme()`, `currentTheme()` — reads/writes `localStorage`
- `matchMedia('prefers-color-scheme: dark')` listener
- All hand-written vanilla JS, duplicated twice (36 lines total)
---
## 19. Inline SVG Icons
**Penguin UI: none (Penguin uses Heroicons-equivalent inline SVGs)**
| # | Location | Icon | Occurrences |
|---|----------|------|-------------|
| 55 | `base.html:70-72,168-170` | Hamburger (3-line menu) | 2 |
| 56 | `base.html:104-105` | Shopping cart | 1 |
| 57 | `base.html:116-121` | Gear/cog (settings) | 1 |
| 58 | ~~`base.html:220-221`~~ | Checkmark (toast success) | ✅ removed — now in vendored toast component |
| 59 | `checkout.html:62-64,115-117` | Chevron-down (dropdown arrow) | 2 |
| 60 | `_sidebar.html:30-33` | Chevron-right (accordion expand) | 1 |
| 61 | `admin/base.html:106-108` | Hamburger (admin sidebar toggle) | 1 |
| 62 | `admin/base.html:121-125` | Gear/cog (admin settings) | 1 |
All are raw inline `<svg>` with hardcoded `<path d="...">` — no icon library, no partials.
---
## 20. Empty State
**Penguin UI: no direct component, but table empty states exist in Penguin tables**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 63 | `assets/views/admin/orders/index.html:38-39` | "No orders" message | ~2 lines |
| 64 | `assets/views/admin/catalog/products.html:72-78` | "No products" with CTA button | ~7 lines |
| 65 | `assets/views/admin/catalog/categories.html:61-67` | "No categories" with CTA button | ~7 lines |
| 66 | `assets/views/shop/_cart_body.html:67-70` | "Cart empty" with CTA button | ~4 lines |
| 67 | `assets/views/shop/_sidebar.html:58-59` | "No categories" message | ~2 lines |
---
## 21. Dashboard Navigation Cards
**Penguin UI: `card/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 68 | `assets/views/admin/index.html:12-27` | 3 dashboard link cards (Products, Categories, Orders) | ~16 lines |
**Details:**
- Each card is an `<a>` styled with border, hover effect, and nested title+description
- Same hover pattern: `hover:border-primary`
---
## 22. Checkout Order Summary
**Penguin UI: `card/` (ecommerce-summary style)**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 69 | `assets/views/shop/checkout.html:190-218` | Cart summary aside: item list, subtotal, shipping, total, place-order button | ~29 lines |
**Details:**
- Item list with name × quantity + line total
- Subtotal + shipping + total with `tabular-nums`
- Dynamic shipping price from Alpine `carrierPrice`
- Disabled submit button when `!canSubmit`
---
## 23. Login Card
**Penguin UI: `card/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 70 | `assets/views/admin/login.html:6-61` | Full login form: header with auth badge, email + password inputs, error alert, submit button | ~56 lines |
---
## 24. Checkout Fieldset Cards
**Penguin UI: `card/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 71 | `assets/views/shop/checkout.html:34-79` | Contact info fieldset (email, name, phone+prefix) | ~46 lines |
| 72 | `assets/views/shop/checkout.html:82-130` | Shipping address fieldset (address, city, zip, country) | ~49 lines |
| 73 | `assets/views/shop/checkout.html:133-165` | Carrier selection fieldset | ~33 lines |
| 74 | `assets/views/shop/checkout.html:167-180` | Payment method fieldset | ~14 lines |
Each fieldset uses `<fieldset>` + `<legend>` with the same `rounded-radius border border-outline bg-surface p-6` styling.
---
## 25. Order Detail Info Panel
**Penguin UI: `card/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 75 | `assets/views/admin/orders/show.html:49-77` | Customer + shipping + payment info panel | ~29 lines |
| 76 | `assets/views/admin/orders/show.html:79-103` | Fulfillment panel (tracking, label link, ship button) | ~25 lines |
| 77 | `assets/views/admin/orders/show.html:106-115` | Status update form panel | ~10 lines |
---
## 26. Shipping Method Settings Row
**Penguin UI: `card/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 78 | `assets/views/admin/shipping/index.html:14-34` | Per-carrier settings: name label, price input, enabled checkbox, save button | ~21 lines |
---
## 27. Product/Category Form Wrapper
**Penguin UI: `card/`**
| # | Location | What it is | Size |
|---|----------|------------|------|
| 79 | `assets/views/admin/catalog/product_form.html:15-99` | Full product edit/create form with all fields | ~84 lines |
| 80 | `assets/views/admin/catalog/category_form.html:15-81` | Full category edit/create form with all fields | ~66 lines |
Both are wrapped in a single card-style `<form>`.
---
## Summary
| # | Component | Penguin UI Directory | Handcoded Instances | Total Lines |
|---|-----------|---------------------|--------------------|-------------|
| 1 | Navbar | `navbar/` | 2 | ~143 |
| 2 | Sidebar (admin) | `sidebar/` | 2 | ~46 |
| 3 | Sidebar (category accordion) | `sidebar/` | 2 | ~62 |
| 4 | Dropdown (settings) | `dropdown-menu/` | **2 duplicates** | ~103 |
| 5 | Country/Phone combobox | `text-input/` | 2 | ~51 |
| 6 | Product card | `card/` | 1 | ~30 |
| 7 | Image gallery | `carousel/` | 1 | ~19 |
| 8 | Radio groups | (form inputs) | 2 | ~47 |
| 9 | Checkbox | `checkbox/` | 3 | ~15 |
| 10 | Text input | `text-input/` | 8 | ~146 |
| 11 | Textarea | `textarea/` | 3 | ~10 |
| 12 | Select | `select/` | 3 | ~23 |
| 13 | File input | `file-input/` | 2 | ~12 |
| 14 | Table | `table/` | 5 | ~196 |
| 15 | Alert/Error | `alert/` | 2 | ~9 |
| 16 | Badge/Pill | `badge/` | 6 | ~17 |
| 17 | Button | `buttons/` | 50+ occurrences | ~200+ |
| 18 | Toggle (theme) | `toggle/` | **2 duplicates** | ~36 |
| 19 | Inline SVG icons | N/A | 8 distinct icons | ~50 |
| 20 | Empty state | (table variants) | 5 | ~22 |
| 21 | Dashboard cards | `card/` | 1 | ~16 |
| 22 | Checkout summary | `card/` | 1 | ~29 |
| 23 | Login card | `card/` | 1 | ~56 |
| 24 | Checkout fieldsets | `card/` | 4 | ~142 |
| 25 | Order info panels | `card/` | 3 | ~64 |
| 26 | Shipping settings row | `card/` | 1 | ~21 |
| 27 | Form wrappers | `card/` | 2 | ~150 |
**Grand total: ~27 distinct handcoded UI component types across ~80 instances, representing approximately 1,600+ lines of handcoded HTML/Tailwind/Alpine that could be replaced by Penguin UI components.**
### Duplication hotspots:
- **Settings dropdown** (`base.html:110-162` and `admin/base.html:117-166`) — 100% copy-paste
- **Theme toggle JS** (`base.html:13-30` and `admin/base.html:13-30`) — 100% copy-paste
- **Text input class string** — same 80-character Tailwind string appears 15+ times
- **Table class strings** (thead, tbody, tr) — copy-pasted 5 times
- **Button variants** — inconsistent `hover:opacity-75` vs `hover:opacity-90`