Double desktop header logo, add English caption under each carousel title

- Header logo (desktop variant) doubled: 40/48px -> 80/96px.
- Each dynamic store-specials carousel now shows an English caption
  under its title at 50% opacity, derived from the group's real `key`
  (e.g. "top-selling" -> "TOP SELLING") rather than inventing a
  translation of the Persian title, which the API doesn't provide.
  New SectionTitle `enSubtitle` prop (separate from the existing
  inline `enTitle`) carries this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
amirhosein.ashourloo
2026-09-15 18:06:42 +03:30
co-authored by Claude Sonnet 5
parent 7c04316f3a
commit 0de5abb879
4 changed files with 42 additions and 3 deletions
+8 -1
View File
@@ -222,7 +222,14 @@ time the page renders, with zero code changes.
`SectionTitle`'s `enTitle` prop is optional for exactly this reason — these `SectionTitle`'s `enTitle` prop is optional for exactly this reason — these
dynamic groups only have one `title` string from the API, not a Persian/ dynamic groups only have one `title` string from the API, not a Persian/
English pair, so the small English-caps subtitle just doesn't render for them. English pair, so the inline English-caps subtitle just doesn't render for
them. Instead, each carousel shows an English caption *under* the title
(`SectionTitle`'s separate `enSubtitle` prop, `opacity-50`, its own line) —
`StoreSpecialCarousels.englishLabelFromKey()` formats the group's `key`
(e.g. `top-selling` → "TOP SELLING") rather than inventing a translation of
`title`; there's no real localized-English field to translate from, and a
made-up translation would be fabricated data. Skips rendering if the key
has no letters at all (fell back to a bare numeric id).
**Banner interleaving**: the two static-image banner sections (`TwoBanners`, **Banner interleaving**: the two static-image banner sections (`TwoBanners`,
`ThreeBanners`) are rendered *inside* `StoreSpecialCarousels`, one inserted `ThreeBanners`) are rendered *inside* `StoreSpecialCarousels`, one inserted
+1 -1
View File
@@ -27,7 +27,7 @@ function Logo({ src, large = false }: { src?: string | null; large?: boolean })
className={ className={
large large
? "h-[42px] w-auto object-contain md:h-[46px]" ? "h-[42px] w-auto object-contain md:h-[46px]"
: "h-10 w-auto object-contain md:h-12" : "h-20 w-auto object-contain md:h-24"
} }
/> />
</a> </a>
@@ -6,6 +6,18 @@ import { TwoBanners } from "@/components/home/TwoBanners";
import { ThreeBanners } from "@/components/home/ThreeBanners"; import { ThreeBanners } from "@/components/home/ThreeBanners";
import { productHref, type ProductGroup, type StaticImageWithUrl } from "@/lib/meshkee"; import { productHref, type ProductGroup, type StaticImageWithUrl } from "@/lib/meshkee";
// The API only gives one `title` string per group (sometimes already
// English, e.g. "Special Sale", sometimes Persian) — there's no separate
// localized English field to pull a real translation from, and inventing
// one would be fabricated data. `key` is real data and usually a readable
// English slug (e.g. "top-selling"), so it's what gets formatted into the
// English caption under the title. Skip it if the key has no letters at
// all (e.g. it fell back to a bare numeric id).
function englishLabelFromKey(key: string): string | undefined {
const label = key.replace(/[-_]+/g, " ").trim();
return /[a-zA-Z]/.test(label) ? label.toUpperCase() : undefined;
}
// Renders every admin-managed carousel from GET /store-specials — whatever // Renders every admin-managed carousel from GET /store-specials — whatever
// groups exist there, in their configured order. Nothing here is keyed off // groups exist there, in their configured order. Nothing here is keyed off
// a specific group id/key/title, so a new carousel created in the Meshkee // a specific group id/key/title, so a new carousel created in the Meshkee
@@ -46,6 +58,7 @@ export function StoreSpecialCarousels({
<div className="mx-auto max-w-7xl px-4"> <div className="mx-auto max-w-7xl px-4">
<SectionTitle <SectionTitle
title={group.title} title={group.title}
enSubtitle={englishLabelFromKey(group.key)}
action="مشاهده همه" action="مشاهده همه"
actionHref={`/products?specialId=${group.id}`} actionHref={`/products?specialId=${group.id}`}
/> />
+20 -1
View File
@@ -1,12 +1,22 @@
type SectionTitleProps = { type SectionTitleProps = {
title: string; title: string;
enTitle?: string; enTitle?: string;
// Shown on its own line under the title (not inline like enTitle), at 50%
// opacity — used for the homepage's dynamic store-specials carousels.
enSubtitle?: string;
subtitle?: string; subtitle?: string;
action?: string; action?: string;
actionHref?: string; actionHref?: string;
}; };
export function SectionTitle({ title, enTitle, subtitle, action, actionHref }: SectionTitleProps) { export function SectionTitle({
title,
enTitle,
enSubtitle,
subtitle,
action,
actionHref,
}: SectionTitleProps) {
return ( return (
<div className="mb-6 flex items-end justify-between gap-4"> <div className="mb-6 flex items-end justify-between gap-4">
<div> <div>
@@ -22,6 +32,15 @@ export function SectionTitle({ title, enTitle, subtitle, action, actionHref }: S
</span> </span>
)} )}
</div> </div>
{enSubtitle && (
<p
className="font-display mt-1 text-xs font-semibold tracking-wide text-foreground uppercase opacity-50 md:text-sm"
lang="en"
dir="ltr"
>
{enSubtitle}
</p>
)}
{subtitle && ( {subtitle && (
<p className="mt-1 text-sm text-muted">{subtitle}</p> <p className="mt-1 text-sm text-muted">{subtitle}</p>
)} )}