manufacturers/[slug]: executive leadership + phone + street address
Renders bb.manufacturer_executives rows as a card grid (photo, name, title, bio excerpt, LinkedIn link). Adds a Headquarters section with the street-level hq_address + phone (tel: link) + contact email. Bio text is now whitespace-preserving so multi-paragraph copy from the admin editor renders correctly.
This commit is contained in:
@@ -14,21 +14,19 @@ async function loadManufacturer(slug: string) {
|
||||
const { data } = await supabase
|
||||
.from("tracked_companies")
|
||||
.select(
|
||||
"id, slug, company_name, bio, bio_source, logo_url, company_website, contact_email, contact_url, hq_city, hq_country, linkedin_url, twitter_handle, youtube_url, press_url, categories, exhibits_nab, exhibits_ibc, mention_count, last_mentioned, featured, ad_status"
|
||||
"id, slug, company_name, bio, bio_source, logo_url, company_website, contact_email, contact_url, phone, hq_address, hq_city, hq_country, linkedin_url, twitter_handle, youtube_url, press_url, categories, exhibits_nab, exhibits_ibc, mention_count, last_mentioned, featured, ad_status"
|
||||
)
|
||||
.eq("slug", slug)
|
||||
.eq("directory_visible", true)
|
||||
.maybeSingle();
|
||||
if (!data) return null;
|
||||
|
||||
// Per-show appearances
|
||||
const { data: shows } = await supabase
|
||||
.from("trade_show_exhibitors")
|
||||
.select("show, show_year, booth, description, url, website")
|
||||
.eq("exhibitor_name", data.company_name)
|
||||
.order("show_year", { ascending: false });
|
||||
|
||||
// Recent BB stories that mention this company (mention_count guides interest)
|
||||
const { data: pressReleases } = await supabase
|
||||
.from("press_releases")
|
||||
.select("title, slug, published_at, source_url")
|
||||
@@ -36,7 +34,19 @@ async function loadManufacturer(slug: string) {
|
||||
.order("published_at", { ascending: false })
|
||||
.limit(10);
|
||||
|
||||
return { company: data, shows: shows || [], pressReleases: pressReleases || [] };
|
||||
const { data: executives } = await supabase
|
||||
.from("manufacturer_executives")
|
||||
.select("id, name, title, photo_url, linkedin_url, bio")
|
||||
.eq("company_id", data.id)
|
||||
.order("sort_order")
|
||||
.order("name");
|
||||
|
||||
return {
|
||||
company: data,
|
||||
shows: shows || [],
|
||||
pressReleases: pressReleases || [],
|
||||
executives: executives || [],
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
@@ -76,7 +86,7 @@ export default async function ManufacturerProfile({
|
||||
const { slug } = await params;
|
||||
const result = await loadManufacturer(slug);
|
||||
if (!result) notFound();
|
||||
const { company, shows, pressReleases } = result;
|
||||
const { company, shows, pressReleases, executives } = result;
|
||||
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
@@ -149,6 +159,11 @@ export default async function ManufacturerProfile({
|
||||
{[company.hq_city, company.hq_country].filter(Boolean).join(", ")}
|
||||
</span>
|
||||
)}
|
||||
{company.phone && (
|
||||
<a href={`tel:${company.phone.replace(/[^\d+]/g, '')}`} className="text-ink/60 hover:text-brand-primary">
|
||||
{company.phone}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap gap-3 text-sm">
|
||||
{company.company_website && (
|
||||
@@ -208,7 +223,7 @@ export default async function ManufacturerProfile({
|
||||
{company.bio && (
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-display font-bold text-ink mb-2">About</h2>
|
||||
<p className="text-ink/80 leading-relaxed">{company.bio}</p>
|
||||
<div className="text-ink/80 leading-relaxed space-y-4 whitespace-pre-line">{company.bio}</div>
|
||||
{company.bio_source && (
|
||||
<p className="text-xs text-ink/40 mt-2">
|
||||
Source: {company.bio_source}
|
||||
@@ -217,6 +232,72 @@ export default async function ManufacturerProfile({
|
||||
</section>
|
||||
)}
|
||||
|
||||
{executives.length > 0 && (
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-display font-bold text-ink mb-4">Executive leadership</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{executives.map((e: any) => (
|
||||
<div key={e.id} className="bg-surface-soft border border-ink/10 rounded-lg p-4 flex gap-3">
|
||||
{e.photo_url ? (
|
||||
/* eslint-disable-next-line @next/next/no-img-element */
|
||||
<img
|
||||
src={e.photo_url}
|
||||
alt={e.name}
|
||||
className="w-16 h-16 rounded-full object-cover bg-ink/5 flex-shrink-0"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-16 h-16 rounded-full bg-ink/5 flex items-center justify-center text-ink/40 font-bold flex-shrink-0">
|
||||
{(e.name || "?").trim().charAt(0).toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-0">
|
||||
<div className="font-semibold text-ink truncate">{e.name}</div>
|
||||
{e.title && <div className="text-xs text-ink/60 mb-1">{e.title}</div>}
|
||||
{e.bio && <p className="text-xs text-ink/70 line-clamp-3 mb-1">{e.bio}</p>}
|
||||
{e.linkedin_url && (
|
||||
<a
|
||||
href={e.linkedin_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-[11px] text-brand-primary hover:underline"
|
||||
>
|
||||
LinkedIn ↗
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{(company.hq_address || company.phone || company.contact_email) && (
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-display font-bold text-ink mb-2">Headquarters</h2>
|
||||
<address className="not-italic text-ink/80 text-sm leading-relaxed">
|
||||
{company.hq_address && <div>{company.hq_address}</div>}
|
||||
{(company.hq_city || company.hq_country) && (
|
||||
<div>{[company.hq_city, company.hq_country].filter(Boolean).join(", ")}</div>
|
||||
)}
|
||||
{company.phone && (
|
||||
<div className="mt-1">
|
||||
<a href={`tel:${company.phone.replace(/[^\d+]/g, '')}`} className="text-brand-primary hover:underline">
|
||||
{company.phone}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
{company.contact_email && (
|
||||
<div>
|
||||
<a href={`mailto:${company.contact_email}`} className="text-brand-primary hover:underline">
|
||||
{company.contact_email}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</address>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{company.categories && company.categories.length > 0 && (
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-display font-bold text-ink mb-2">Categories</h2>
|
||||
|
||||
Reference in New Issue
Block a user