Update carousels, card hover, and IranBay branding.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-04 13:21:51 +03:30
co-authored by Cursor
commit 42aff5aee9
162 changed files with 16828 additions and 0 deletions
+455
View File
@@ -0,0 +1,455 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import type { BusinessDetail, BusinessTabId } from "@/data/business";
import { businessTabs } from "@/data/business";
import styles from "./BusinessDetail.module.css";
function Stars({ rating }: { rating: string }) {
return (
<div className={styles.stars} aria-label={`${rating} out of 5`}>
{Array.from({ length: 5 }, (_, i) => (
<span
key={i}
className={i < 4 ? styles.starFilled : styles.starEmpty}
aria-hidden
>
★
</span>
))}
<span className={styles.rating}>{rating}</span>
</div>
);
}
function CategoryPills({
items,
active,
onChange,
}: {
items: string[];
active: string;
onChange: (value: string) => void;
}) {
return (
<div className={styles.pills}>
{items.map((item) => (
<button
key={item}
type="button"
className={`filter-chip${item === active ? " is-active" : ""}`}
aria-pressed={item === active}
onClick={() => onChange(item)}
>
{item}
</button>
))}
</div>
);
}
function FiltersBar({
fields,
tags,
}: {
fields: { label: string; value: string }[];
tags: { label: string; value: string }[];
}) {
return (
<>
<div className={styles.filtersBar}>
<div className={styles.filtersLabel}>
<img src="/images/icons/search.svg" alt="" width={22} height={22} />
<div>
<strong>Filters</strong>
<span>A better focus</span>
</div>
</div>
<div className={styles.filters}>
{fields.map((field) => (
<label key={field.label} className={styles.filterField}>
<span>{field.label}</span>
<input defaultValue={field.value} readOnly />
</label>
))}
<button className={`filter-chip ${styles.filterBtn}`} type="button">
Filter
<img src="/images/icons/chevron.svg" alt="" />
</button>
</div>
</div>
{tags.length ? (
<div className={styles.activeTags}>
{tags.map((tag) => (
<span className={`filter-chip ${styles.activeTag}`} key={tag.label}>
<em>{tag.label}:</em> {tag.value}
<button type="button" aria-label={`Remove ${tag.label}`}>
×
</button>
</span>
))}
</div>
) : null}
</>
);
}
export default function BusinessDetailView({
business,
}: {
business: BusinessDetail;
}) {
const [tab, setTab] = useState<BusinessTabId>("products");
const [productCategory, setProductCategory] = useState("All");
const [blogCategory, setBlogCategory] = useState("All");
const [portfolioCategory, setPortfolioCategory] = useState("All");
const [hoveredPortfolio, setHoveredPortfolio] = useState<string | null>(
business.portfolios[2]?.id ?? null,
);
const products =
productCategory === "All"
? business.products
: business.products.filter((p) => p.category === productCategory);
return (
<div className={styles.root}>
<section className={styles.bannerWrap}>
<div className={styles.banner}>
<h1 className={styles.bannerTitle}>{business.name}</h1>
<nav className={styles.crumbs} aria-label="Breadcrumb">
{business.breadcrumbs.map((crumb, index) => (
<span key={`${crumb}-${index}`}>
{index > 0 ? (
<img
className={styles.crumbChevron}
src="/images/icons/chevron.svg"
alt=""
/>
) : null}
<span
className={
index === business.breadcrumbs.length - 1
? styles.crumbActive
: undefined
}
>
{crumb}
</span>
</span>
))}
</nav>
</div>
</section>
<section className={styles.hero}>
<Image
src={business.hero}
alt=""
fill
priority
className={styles.heroImg}
sizes="100vw"
/>
</section>
<section className={styles.profile}>
<div className={styles.profileBar}>
<div className={styles.identity}>
<img
className={styles.logo}
src={business.logo}
alt=""
width={75}
height={75}
/>
<div>
<h2 className={styles.name}>{business.name}</h2>
<p className={styles.tagline}>{business.tagline}</p>
</div>
</div>
<div className={styles.stats}>
<div className={styles.stat}>
<span className={styles.statLabel}>since</span>
<strong className={styles.statValueSm}>{business.since}</strong>
</div>
<div className={styles.stat}>
<span className={styles.statLabel}>province</span>
<strong className={styles.statValueSm}>{business.province}</strong>
</div>
<div className={styles.statWide}>
<strong className={styles.statValueLg}>
{business.activeProducts}
</strong>
<span className={styles.statLabelStack}>
active
<br />
product
</span>
</div>
<div className={styles.stat}>
<span className={styles.statLabel}>Telephone</span>
<strong className={styles.statValueSm}>{business.telephone}</strong>
</div>
</div>
</div>
<p className={styles.description}>{business.description}</p>
</section>
<nav className={styles.tabs} aria-label="Business sections">
{businessTabs.map((item) => (
<button
key={item.id}
type="button"
className={tab === item.id ? styles.tabActive : styles.tab}
onClick={() => setTab(item.id)}
>
{item.label}
</button>
))}
</nav>
<section className={styles.panel} aria-live="polite">
{tab === "products" ? (
<>
<CategoryPills
items={business.productCategories}
active={productCategory}
onChange={setProductCategory}
/>
<FiltersBar
fields={[{ label: "Product name", value: "gold" }]}
tags={[{ label: "Name", value: "gold" }]}
/>
<div className={styles.productGrid}>
{products.map((product) => (
<article key={product.id} className={styles.productCard}>
<div className={styles.productMedia}>
<Image
src={product.image}
alt=""
fill
className={styles.productImg}
sizes="(max-width: 720px) 50vw, 292px"
/>
</div>
<div className={styles.productBody}>
<h3>{product.title}</h3>
<Stars rating={product.rating} />
<div className={styles.productMeta}>
<span className={styles.chip}>
<strong>{product.minOrder}</strong>
<em>
pcs, min
<br />
order
</em>
</span>
<span className={styles.chip}>
<strong>{product.shipsIn}</strong>
<em>
days
<br />
to ship
</em>
</span>
</div>
</div>
</article>
))}
</div>
</>
) : null}
{tab === "contact" ? (
<div className={styles.contact}>
<div className={styles.contactRows}>
<div className={styles.contactRow}>
<span className={styles.contactIcon} aria-hidden>
⌖
</span>
<div>
<span className={styles.contactLabel}>Address</span>
<p>{business.contact.address}</p>
</div>
</div>
<div className={styles.contactRow}>
<span className={styles.contactIcon} aria-hidden>
☎
</span>
<div>
<span className={styles.contactLabel}>land line</span>
<p>{business.contact.landlines.join(" , ")}</p>
</div>
</div>
</div>
<div className={styles.socials}>
{business.contact.socials.map((social) => (
<a
key={social.network}
href="#"
className={styles.socialChip}
>
<img src={social.icon} alt="" width={18} height={18} />
<span>
<em>{social.network}</em> {social.handle}
</span>
</a>
))}
</div>
<div className={styles.map}>
<Image
src={business.contact.map}
alt="Business location map"
fill
className={styles.mapImg}
sizes="100vw"
/>
</div>
</div>
) : null}
{tab === "blog" ? (
<>
<CategoryPills
items={business.blogCategories}
active={blogCategory}
onChange={setBlogCategory}
/>
<FiltersBar
fields={[
{ label: "Article Name", value: "gold Scarlet necklace" },
{ label: "Date", value: "1402.05.12" },
]}
tags={[
{ label: "Name", value: "gold Scarlet necklace" },
{ label: "date", value: "1402.05.12" },
]}
/>
<div className={styles.blogGrid}>
{business.posts.map((post) => (
<Link
key={post.id}
href={`/businesses/${business.slug}/blog/${post.slug}`}
className={styles.blogCard}
>
<div className={styles.blogMedia}>
<Image
src={post.image}
alt=""
fill
className={styles.blogImg}
sizes="(max-width: 720px) 50vw, 292px"
/>
</div>
<div className={styles.blogBody}>
<h3>{post.title}</h3>
<p>{post.excerpt}</p>
<div className={styles.blogMeta}>
<span>
<img
src="/images/icons/home-outline.svg"
alt=""
width={14}
height={14}
/>
{post.date}
</span>
<span>
<img
src="/images/icons/user.svg"
alt=""
width={14}
height={14}
/>
{post.author}
</span>
</div>
</div>
</Link>
))}
</div>
</>
) : null}
{tab === "gallery" ? (
<div className={styles.gallery}>
{business.gallery.map((item) => (
<figure
key={item.id}
className={[
styles.galleryItem,
item.tall ? styles.galleryTall : "",
item.medium ? styles.galleryMedium : "",
]
.filter(Boolean)
.join(" ")}
>
<Image
src={item.image}
alt=""
fill
className={styles.galleryImg}
sizes="(max-width: 900px) 50vw, 20vw"
/>
<span className={styles.galleryZoom} aria-hidden>
<img src="/images/icons/search.svg" alt="" width={22} height={22} />
</span>
</figure>
))}
</div>
) : null}
{tab === "portfolios" ? (
<>
<CategoryPills
items={business.portfolioCategories}
active={portfolioCategory}
onChange={setPortfolioCategory}
/>
<div className={styles.portfolioGrid}>
{business.portfolios.map((item) => {
const showOverlay =
hoveredPortfolio === item.id ||
(hoveredPortfolio === null &&
item.id === business.portfolios[2]?.id);
return (
<Link
key={item.id}
href={`/businesses/${business.slug}/portfolios/${item.slug}`}
className={styles.portfolioCard}
onMouseEnter={() => setHoveredPortfolio(item.id)}
onMouseLeave={() => setHoveredPortfolio(null)}
>
<div className={styles.portfolioMedia}>
<Image
src={item.image}
alt=""
fill
className={styles.portfolioImg}
sizes="(max-width: 900px) 100vw, 33vw"
/>
<div
className={[
styles.portfolioOverlay,
showOverlay ? styles.portfolioOverlayVisible : "",
]
.filter(Boolean)
.join(" ")}
>
<h3>{item.title}</h3>
<p>{item.description}</p>
</div>
</div>
</Link>
);
})}
</div>
</>
) : null}
</section>
</div>
);
}