mirror of
https://git.meshkee.com/novintrades/website.git
synced 2026-08-12 05:00:58 +04:30
Initial commit: NovinTrades website monorepo.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { MapPin } from "lucide-react";
|
||||
import { fetchLatestBrands } from "../lib/services";
|
||||
import type { Brand } from "../lib/types";
|
||||
import "./LatestBrands.css";
|
||||
|
||||
export default function LatestBrands() {
|
||||
const [brands, setBrands] = useState<Brand[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let active = true;
|
||||
fetchLatestBrands(4)
|
||||
.then((items) => {
|
||||
if (active) setBrands(items);
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
if (active) {
|
||||
setError(err instanceof Error ? err.message : "Could not load brands");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (active) setLoading(false);
|
||||
});
|
||||
return () => {
|
||||
active = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="section brands" id="brands">
|
||||
<div className="container">
|
||||
<div className="section__head">
|
||||
<span className="section__eyebrow">Partner directory</span>
|
||||
<h2 className="section__title">Latest brands</h2>
|
||||
<p className="section__lead">
|
||||
Newly featured companies ready to connect across markets and industries.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{loading ? <p className="brands__status">Loading brands…</p> : null}
|
||||
{error ? (
|
||||
<p className="brands__status brands__status--error">{error}</p>
|
||||
) : null}
|
||||
{!loading && !error && brands.length === 0 ? (
|
||||
<p className="brands__status">No published brands yet.</p>
|
||||
) : null}
|
||||
|
||||
<div className="brands__grid">
|
||||
{brands.map((brand) => (
|
||||
<Link
|
||||
key={brand.id}
|
||||
className="brands__item"
|
||||
to={`/brands/${brand.slug}`}
|
||||
>
|
||||
<div className="brands__media">
|
||||
{brand.imageUrl ? (
|
||||
<img src={brand.imageUrl} alt="" loading="lazy" />
|
||||
) : (
|
||||
<span className="brands__placeholder" aria-hidden="true" />
|
||||
)}
|
||||
</div>
|
||||
<div className="brands__body">
|
||||
<h3>{brand.title}</h3>
|
||||
{brand.abstract ? <p>{brand.abstract}</p> : null}
|
||||
<span className="brands__loc">
|
||||
<MapPin size={14} strokeWidth={2} />
|
||||
{[brand.city, brand.country].filter(Boolean).join(", ")}
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user