user-app: redesigned home screen and product detail

Design language introduced here and reused across both screens:
  - LESS colour tokens (@ink/@inkSoft/@inkMuted/@inkFaint, @wine/@wineSoft,
    @canvas/@canvasSoft, @hairline/@hairlineSoft) added to
    themeConfig/colors.json and injected globally via vite's modifyVars, so
    scoped <style lang="less"> blocks resolve them with no import
  - BEM-ish scoped class names, hairline-bordered cards at 14-18px radii,
    ink surfaces with a wine accent, dashed-border empty states
  - components/home/: SectionHead, ShowcaseCard, ShowcaseSlider, CategoryRail,
    RateStrip, AccountStrip

Data honesty is deliberate throughout: no rial price is printed for a piece,
because a finished item's price is the live gold rate x weight plus wage,
computed at order time and never stored on the product. Only genuinely
present fields are shown (weight, making-charge percent, stock, category,
brand). Categories and the hero are derived from the real catalogue, so an
empty catalogue renders an empty state rather than placeholder tiles.

Also fixes a real bug in the product detail view: it ran, at module scope,
history.pushState + window.onpopstate = () => history.go(1). Because the
drawer mounts its slot eagerly, that fired for every product card on the
home screen and permanently hijacked the back button for the session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
s.esmaeili
2026-09-14 23:05:29 +03:30
co-authored by Claude Opus 5
parent a12fb0450e
commit f2d83c53df
41 changed files with 2041 additions and 241 deletions
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F6F3EF"/>
<g transform="translate(0,0)" color="#8D6A46" opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="14" fill="none" stroke="currentColor" stroke-width="4"/><ellipse cx="32" cy="32" rx="14" ry="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 415 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F3EADC"/>
<g transform="translate(0,0)" color="#8D6A46" opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="14" fill="none" stroke="currentColor" stroke-width="4"/><ellipse cx="32" cy="32" rx="14" ry="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 415 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#FAF5EC"/>
<g transform="translate(0,0)" color="#8D6A46" opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="14" fill="none" stroke="currentColor" stroke-width="4"/><ellipse cx="32" cy="32" rx="14" ry="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 415 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F0E6D6"/>
<g transform="translate(0,0)" color="#8D6A46" opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="14" fill="none" stroke="currentColor" stroke-width="4"/><ellipse cx="32" cy="32" rx="14" ry="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 415 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F8F1E6"/>
<g transform="translate(0,0)" color="#8D6A46" opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="14" fill="none" stroke="currentColor" stroke-width="4"/><ellipse cx="32" cy="32" rx="14" ry="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 415 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#EFE4D3"/>
<g transform="translate(0,0)" color="#8D6A46" opacity="0.92">
<ellipse cx="32" cy="32" rx="22" ry="14" fill="none" stroke="currentColor" stroke-width="4"/><ellipse cx="32" cy="32" rx="14" ry="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 415 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F6F3EF"/>
<g transform="translate(0,0)" color="#D4AF37" opacity="0.92">
<circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="32" r="14" fill="none" stroke="currentColor" stroke-width="2" opacity="0.6"/><text x="32" y="38" font-size="14" text-anchor="middle" fill="currentColor" font-family="sans-serif">Au</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 506 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F3EADC"/>
<g transform="translate(0,0)" color="#D4AF37" opacity="0.92">
<circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="32" r="14" fill="none" stroke="currentColor" stroke-width="2" opacity="0.6"/><text x="32" y="38" font-size="14" text-anchor="middle" fill="currentColor" font-family="sans-serif">Au</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 506 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#FAF5EC"/>
<g transform="translate(0,0)" color="#D4AF37" opacity="0.92">
<circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="32" r="14" fill="none" stroke="currentColor" stroke-width="2" opacity="0.6"/><text x="32" y="38" font-size="14" text-anchor="middle" fill="currentColor" font-family="sans-serif">Au</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 506 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F0E6D6"/>
<g transform="translate(0,0)" color="#D4AF37" opacity="0.92">
<circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="32" r="14" fill="none" stroke="currentColor" stroke-width="2" opacity="0.6"/><text x="32" y="38" font-size="14" text-anchor="middle" fill="currentColor" font-family="sans-serif">Au</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 506 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F8F1E6"/>
<g transform="translate(0,0)" color="#D4AF37" opacity="0.92">
<circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="32" r="14" fill="none" stroke="currentColor" stroke-width="2" opacity="0.6"/><text x="32" y="38" font-size="14" text-anchor="middle" fill="currentColor" font-family="sans-serif">Au</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 506 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#EFE4D3"/>
<g transform="translate(0,0)" color="#D4AF37" opacity="0.92">
<circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="32" r="14" fill="none" stroke="currentColor" stroke-width="2" opacity="0.6"/><text x="32" y="38" font-size="14" text-anchor="middle" fill="currentColor" font-family="sans-serif">Au</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 506 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F6F3EF"/>
<g transform="translate(0,0)" color="#A67C52" opacity="0.92">
<circle cx="32" cy="16" r="5" fill="currentColor"/><path d="M32 21 L32 34" stroke="currentColor" stroke-width="3"/><circle cx="32" cy="42" r="10" fill="none" stroke="currentColor" stroke-width="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 414 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F3EADC"/>
<g transform="translate(0,0)" color="#A67C52" opacity="0.92">
<circle cx="32" cy="16" r="5" fill="currentColor"/><path d="M32 21 L32 34" stroke="currentColor" stroke-width="3"/><circle cx="32" cy="42" r="10" fill="none" stroke="currentColor" stroke-width="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 414 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#FAF5EC"/>
<g transform="translate(0,0)" color="#A67C52" opacity="0.92">
<circle cx="32" cy="16" r="5" fill="currentColor"/><path d="M32 21 L32 34" stroke="currentColor" stroke-width="3"/><circle cx="32" cy="42" r="10" fill="none" stroke="currentColor" stroke-width="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 414 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F0E6D6"/>
<g transform="translate(0,0)" color="#A67C52" opacity="0.92">
<circle cx="32" cy="16" r="5" fill="currentColor"/><path d="M32 21 L32 34" stroke="currentColor" stroke-width="3"/><circle cx="32" cy="42" r="10" fill="none" stroke="currentColor" stroke-width="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 414 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F8F1E6"/>
<g transform="translate(0,0)" color="#A67C52" opacity="0.92">
<circle cx="32" cy="16" r="5" fill="currentColor"/><path d="M32 21 L32 34" stroke="currentColor" stroke-width="3"/><circle cx="32" cy="42" r="10" fill="none" stroke="currentColor" stroke-width="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 414 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#EFE4D3"/>
<g transform="translate(0,0)" color="#A67C52" opacity="0.92">
<circle cx="32" cy="16" r="5" fill="currentColor"/><path d="M32 21 L32 34" stroke="currentColor" stroke-width="3"/><circle cx="32" cy="42" r="10" fill="none" stroke="currentColor" stroke-width="3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 414 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F6F3EF"/>
<g transform="translate(0,0)" color="#C9A24B" opacity="0.92">
<path d="M14 14 C14 34 24 44 32 44 C40 44 50 34 50 14" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 44 L32 52 L26 58 L38 58 Z" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 384 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F3EADC"/>
<g transform="translate(0,0)" color="#C9A24B" opacity="0.92">
<path d="M14 14 C14 34 24 44 32 44 C40 44 50 34 50 14" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 44 L32 52 L26 58 L38 58 Z" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 384 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#FAF5EC"/>
<g transform="translate(0,0)" color="#C9A24B" opacity="0.92">
<path d="M14 14 C14 34 24 44 32 44 C40 44 50 34 50 14" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 44 L32 52 L26 58 L38 58 Z" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 384 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F0E6D6"/>
<g transform="translate(0,0)" color="#C9A24B" opacity="0.92">
<path d="M14 14 C14 34 24 44 32 44 C40 44 50 34 50 14" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 44 L32 52 L26 58 L38 58 Z" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 384 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F8F1E6"/>
<g transform="translate(0,0)" color="#C9A24B" opacity="0.92">
<path d="M14 14 C14 34 24 44 32 44 C40 44 50 34 50 14" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 44 L32 52 L26 58 L38 58 Z" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 384 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#EFE4D3"/>
<g transform="translate(0,0)" color="#C9A24B" opacity="0.92">
<path d="M14 14 C14 34 24 44 32 44 C40 44 50 34 50 14" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 44 L32 52 L26 58 L38 58 Z" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 384 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F6F3EF"/>
<g transform="translate(0,0)" color="#B08D57" opacity="0.92">
<circle cx="32" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="20" r="6" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 350 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F3EADC"/>
<g transform="translate(0,0)" color="#B08D57" opacity="0.92">
<circle cx="32" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="20" r="6" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 350 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#FAF5EC"/>
<g transform="translate(0,0)" color="#B08D57" opacity="0.92">
<circle cx="32" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="20" r="6" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 350 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F0E6D6"/>
<g transform="translate(0,0)" color="#B08D57" opacity="0.92">
<circle cx="32" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="20" r="6" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 350 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#F8F1E6"/>
<g transform="translate(0,0)" color="#B08D57" opacity="0.92">
<circle cx="32" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="20" r="6" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 350 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="#EFE4D3"/>
<g transform="translate(0,0)" color="#B08D57" opacity="0.92">
<circle cx="32" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="20" r="6" fill="currentColor"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 350 B