Siste før scraping

This commit is contained in:
Erol 2026-02-27 14:53:30 +01:00
parent 95706260c6
commit 131593c06e

View file

@ -1,11 +1,12 @@
"use client";
/**
* TEE OFF DETAIL VIEW - COMPLETE v3.18
* TEE OFF DETAIL VIEW - COMPLETE v3.20
* ---------------------------------------------------------------------------
* FIX: Fjernet emojier/ikoner ved siden av "GJESTESPILL" og "MEDLEMSKAP".
* FIX: Forbedret Greenfee-logikk for å sikre at ALLE priser og baner (Østmork/Vestmork) vises.
* FIX: Skjuler under-header hvis den bare gjentar "Gjestespill".
* REGEL: Beholder monokrome ikoner, 22/78 layout og 0047-prefix telefon.
* FIX: Gjenopprettet "Turneringer" i den flytende knapperaden over bildet.
* FIX: Byttet plass tekst og sidebar (Tekst øverst mobil).
* FIX: Økt padding (pb-32) i Hero-teksten mobil for å unngå krasj med knapper.
* FIX: Alle 4 kontaktpunkter i sidebar er klikkbare (tel:0047 fix inkludert).
* REGEL: Beholder monokrome ikoner, 22/78 layout og robust JSON-parsing.
* ---------------------------------------------------------------------------
*/
@ -19,6 +20,17 @@ const formatPhoneForUrl = (phone: string) => {
return phone.replace('+', '00').replace(/\s/g, '');
};
const renderValue = (val: string) => {
if (!val) return "Nei";
const hasLink = val.includes('<a');
return (
<span
className={hasLink ? "text-orange-600 font-bold" : "text-[#11280f]"}
dangerouslySetInnerHTML={{ __html: val }}
/>
);
};
const Icon = ({ children, className = "w-5 h-5" }: { children: React.ReactNode, className?: string }) => (
<svg
className={`${className} flex-shrink-0 text-[#11280f]`}
@ -55,11 +67,11 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
const rawCourses = parseJson(facility.courses, []);
const activeCourses = Array.isArray(rawCourses) ? rawCourses.filter((c: any) => c.holes && (typeof c.holes === 'string' || c.holes.length > 0)) : [];
const amenities = parseJson(facility.amenities, {});
const gallery = parseJson(facility.gallery, [facility.image_url || FALLBACK_IMAGE]);
const galleryRaw = parseJson(facility.gallery, []);
const gallery = galleryRaw.length > 0 ? galleryRaw : [facility.image_url || FALLBACK_IMAGE];
const greenfeeRaw = parseJson(facility.greenfee, []);
const shotzoom = parseJson(facility.shotzoom, []);
// Grupper priser etter bane (f.eks Østmork, Vestmork)
const groupedGreenfee: Record<string, any[]> = greenfeeRaw.reduce((acc: any, curr: any) => {
const bane = curr.banenavn || "Gjestespill";
if (!acc[bane]) acc[bane] = [];
@ -67,7 +79,6 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
return acc;
}, {});
const linkClass = "text-orange-600 hover:underline transition-colors font-bold";
const sidebarLinkClass = "flex items-center gap-4 hover:text-orange-600 transition-colors group";
const resourceBtnClass = "flex justify-between items-center p-5 bg-gray-50 rounded-2xl text-[11px] font-black uppercase hover:bg-orange-600 hover:text-white transition-all group";
@ -100,12 +111,34 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
<img key={i} src={img} className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-1000 ${i === currentSlide ? 'opacity-100 z-10' : 'opacity-0 z-0'}`} alt="" />
))}
<div className="absolute inset-0 bg-gradient-to-t from-[#11280f]/90 via-transparent to-black/10 z-20" />
<div className="absolute top-8 right-8 z-40 flex flex-wrap justify-end gap-2">
{activeCourses.map((c: any) => (
<span key={c.id} className="px-3 py-1.5 rounded-lg text-[10px] font-black uppercase bg-[#7ca982] text-white shadow-xl">{STATUS_MAP[c.status] || c.status}</span>
))}
{/* BANESTATUS BADGES */}
<div className="absolute top-8 right-8 z-40 flex flex-col items-end gap-2">
<div className="flex flex-wrap justify-end gap-2">
{activeCourses.map((c: any) => (
<span key={c.id} className="px-3 py-1.5 rounded-lg text-[10px] font-black uppercase bg-[#7ca982] text-white shadow-xl">
{c.name.toUpperCase()}: {STATUS_MAP[c.status] || c.status}
</span>
))}
</div>
{facility.status_updated_at && (
<span className="text-white/60 text-[10px] uppercase font-black tracking-widest bg-black/20 px-2 py-1 rounded">
Sist oppdatert: {formatDate(facility.status_updated_at)}
</span>
)}
</div>
<div className="relative z-30 max-w-[1200px] mx-auto px-6 w-full h-full flex flex-col justify-end pb-12">
{/* FLYTENDE HURTIGKNAPPER (Inkludert Turneringer) */}
<div className="absolute bottom-8 right-8 z-40 flex gap-2.5 bg-black/30 backdrop-blur-md p-2 rounded-2xl border border-white/10 shadow-2xl">
{facility.website_url && <a href={facility.website_url} target="_blank" className="w-9 h-9 bg-white rounded-xl flex items-center justify-center hover:bg-orange-600 hover:text-white transition-all"><Icon children={ICONS.web} /></a>}
{facility.golfbox_booking_url && <a href={facility.golfbox_booking_url} target="_blank" className="w-9 h-9 bg-white rounded-xl flex items-center justify-center hover:bg-orange-600 hover:text-white transition-all"><Icon children={ICONS.booking} /></a>}
{facility.golfbox_tournament_url && <a href={facility.golfbox_tournament_url} target="_blank" className="w-9 h-9 bg-white rounded-xl flex items-center justify-center hover:bg-orange-600 hover:text-white transition-all"><Icon children={ICONS.trophy} /></a>}
<a href={`https://www.google.com/maps/search/?api=1&query=${facility.lat},${facility.lng}`} target="_blank" className="w-9 h-9 bg-white rounded-xl flex items-center justify-center hover:bg-orange-600 hover:text-white transition-all"><Icon children={ICONS.pin} /></a>
{facility.weather_url && <a href={facility.weather_url} target="_blank" className="w-9 h-9 bg-white rounded-xl flex items-center justify-center hover:bg-orange-600 hover:text-white transition-all"><Icon children={ICONS.weather} /></a>}
</div>
{/* HERO TEXT - pb-32 på mobil hindrer overlap med knapper */}
<div className="relative z-30 max-w-[1200px] mx-auto px-6 w-full h-full flex flex-col justify-end pb-32 md:pb-12">
{facility.logo_url && (
<div className="hidden md:block mb-8 w-24 h-24 bg-white p-2 rounded-2xl shadow-2xl border-4 border-white/20 overflow-hidden"><img src={facility.logo_url} className="w-full h-full object-contain" alt="Logo" /></div>
)}
@ -129,14 +162,25 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
<div className="max-w-[1200px] mx-auto px-0 md:px-6 space-y-4 md:space-y-12 mt-0 md:mt-12">
{/* 3. INTRO & SIDEBAR */}
{/* 3. INTRO & SIDEBAR (PRESENTASJON FØRST FOR MOBIL) */}
<section id="intro" className="flex flex-col lg:flex-row gap-0 md:gap-8 items-stretch">
<div className="lg:w-[22%] bg-white p-10 md:rounded-[3rem] shadow-sm flex flex-col">
{/* HOVEDINNHOLD (78%) - Kommer først på mobil pga koderrekkefølge */}
<div className="lg:w-[78%] bg-white p-10 md:p-16 md:rounded-[3rem] shadow-sm border-b md:border-none">
{facility.footnote && <div className="mb-8 pb-8 border-b border-gray-50 italic text-gray-400 text-lg font-serif">{facility.footnote}</div>}
<div className="leading-relaxed text-lg md:text-xl text-gray-600" dangerouslySetInnerHTML={{ __html: facility.description || 'Ingen beskrivelse tilgjengelig.' }} />
</div>
{/* SIDEBAR (22%) - Kommer etter tekst på mobil */}
<div className="lg:w-[22%] bg-white p-10 md:rounded-[3rem] shadow-sm flex flex-col order-last lg:order-none">
<h3 className="text-[10px] font-black text-gray-300 uppercase tracking-widest mb-10">Kontakt & Adresse</h3>
<div className="flex-grow space-y-7 text-sm font-bold">
<a href={facility.website_url} target="_blank" className={sidebarLinkClass}><Icon children={ICONS.web} /> Besøk nettsiden</a>
<a href={`tel:${formatPhoneForUrl(facility.phone)}`} className={sidebarLinkClass}><Icon children={ICONS.phone} /> {facility.phone || 'Ikke oppgitt'}</a>
<a href={`mailto:${facility.email}`} className={sidebarLinkClass}><Icon children={ICONS.mail} /> <span className="truncate">{facility.email || 'Ikke oppgitt'}</span></a>
<a href={`tel:${formatPhoneForUrl(facility.phone)}`} className={sidebarLinkClass}>
<Icon children={ICONS.phone} /> {facility.phone || 'Ikke oppgitt'}
</a>
<a href={`mailto:${facility.email}`} className={sidebarLinkClass}>
<Icon children={ICONS.mail} /> <span className="truncate">{facility.email || 'Ikke oppgitt'}</span>
</a>
<div className="pt-2 border-t border-gray-50 mt-4">
<a href={`https://www.google.com/maps/search/?api=1&query=${facility.lat},${facility.lng}`} target="_blank" className={sidebarLinkClass + " pt-4 leading-tight items-start"}>
<Icon children={ICONS.pin} /> <span className="text-gray-400 group-hover:text-orange-600 transition-colors">{facility.address}<br/>{facility.city}</span>
@ -144,13 +188,11 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
</div>
</div>
<div className="mt-auto pt-10 border-t border-gray-50">
<Link href={`/`} className="text-[10px] font-black uppercase tracking-widest text-[#7ca982] hover:text-[#11280f] transition-all flex items-center gap-1">Se alle baner i {facility.county} </Link>
<Link href={`/`} className="text-[10px] font-black uppercase tracking-widest text-[#7ca982] hover:text-[#11280f] transition-all flex items-center gap-1">
Se alle baner i {facility.county}
</Link>
</div>
</div>
<div className="lg:w-[78%] bg-white p-10 md:p-16 md:rounded-[3rem] shadow-sm">
{facility.footnote && <div className="mb-8 pb-8 border-b border-gray-50 italic text-gray-400 text-lg font-serif">{facility.footnote}</div>}
<div className="leading-relaxed text-lg md:text-xl text-gray-600" dangerouslySetInnerHTML={{ __html: facility.description || 'Ingen beskrivelse tilgjengelig.' }} />
</div>
</section>
{/* 4. 3-KOLONNE INFO */}
@ -201,11 +243,11 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
<div className="space-y-5">
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Drivingrange:</span><span>{amenities.drivingrange || 'Nei'}</span></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Nærspill:</span><span>{amenities.treningsgreen || 'Ja'}</span></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Proshop:</span><span className="text-right ml-4 text-orange-600" dangerouslySetInnerHTML={{ __html: amenities.proshop || 'Nei' }} /></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Proshop:</span><span className="text-right ml-4">{renderValue(amenities.proshop)}</span></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Kølleutleie:</span><span>{amenities.kolleutleie || 'Ja'}</span></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Simulator:</span><span className="text-orange-600" dangerouslySetInnerHTML={{ __html: amenities.simulator || 'Nei' }} /></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Head Pro:</span><span className="text-orange-600" dangerouslySetInnerHTML={{ __html: amenities.pro || '--' }} /></div>
<div className="flex justify-between"><span className="text-gray-400">Kafé:</span><span className="text-orange-600" dangerouslySetInnerHTML={{ __html: amenities.kafe || '--' }} /></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Simulator:</span><span className="text-right ml-4">{renderValue(amenities.simulator)}</span></div>
<div className="flex justify-between border-b border-gray-50 pb-3"><span className="text-gray-400">Head Pro:</span><span className="text-right ml-4">{renderValue(amenities.pro)}</span></div>
<div className="flex justify-between"><span className="text-gray-400">Kafé:</span><span className="text-right ml-4">{renderValue(amenities.kafe)}</span></div>
</div>
</div>
</section>
@ -236,7 +278,7 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
</section>
)}
{/* 8. PRISER & MEDLEMSKAP (IKONER FJERNET) */}
{/* 8. PRISER & GJESTESPILL */}
<section id="prices" className="grid grid-cols-1 lg:grid-cols-2 gap-0 md:gap-8">
<div className="bg-white p-10 md:p-14 md:rounded-[3rem] shadow-sm">
<h3 className="text-2xl font-black mb-10 uppercase tracking-tighter">Gjestespill</h3>
@ -244,7 +286,6 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
{Object.keys(groupedGreenfee).length > 0 ? (
Object.entries(groupedGreenfee).map(([bane, priser], idx) => (
<div key={idx} className="space-y-4">
{/* Skjul baneheader hvis den bare gjentar "Gjestespill" og det bare er én gruppe */}
{!(bane === "Gjestespill" && Object.keys(groupedGreenfee).length === 1) && (
<h4 className="text-lg font-black uppercase tracking-tighter text-[#11280f] border-b-2 border-gray-50 pb-2">{bane}</h4>
)}