Før endringer i banckend. Hallusinerer A?

This commit is contained in:
Erol 2026-03-13 08:28:17 +01:00
parent 15aee27e24
commit d8e53bf7d1
7 changed files with 594 additions and 64 deletions

87
backend/GreenFee.txt Normal file
View file

@ -0,0 +1,87 @@
https://arendalgk.no/gjester/greenfee-og-booking
https://askergolf.no/gjester/priser-og-booking
https://askimgk.no/gjester
https://atlungstadgolf.no/greenfee/
https://austraattgolf.no/?page_id=4587
https://ballerud.no/informasjon/priser
https://bamblegolfklubb.no/greenfee/
https://www.bgk.no/greenfee
https://www.bjgk.no/gjester/greenfee
https://bjaavanngk.no/gjester/priser-2026
https://www.bodogolfklubb.no/klubben/medlemsskap-2024
https://borregb.no/gjest/informasjon-til-gjestespillere
https://borregaardgk.no/gjester/greenfee/
https://www.bynesetgolf.no/gjestespill/greenfee/
https://bmgk.no/gjester/greenfee
https://drammengk.no/gjester/greenfee
https://drobakgolf.no/gjester/greenfee
https://www.eikergolf.no
https://www.ekholtgolf.no/gjester
https://elverumgolf.no/greenfee/
https://fanagolf.no/banen/reservasjon-av-spilletid
https://www.fetgk.no/klubben/
https://www.florogolf.no/priser/
https://www.frostagk.no/next/p/69980/greenfee
https://gfgk.no/gjester/greenfee
https://giskegolf.no/gjestespill-2/
https://gjerdrumgolfklubb.no/gjester
https://gjersjoengolf.no/gjester
https://gtgk.no/gjestespill/gjestespill
https://grenlandgolf.no/gjester-2/greenfee/
https://grimstadgolfklubb.no/gjester/greenfee
https://grinigolfklubb.no/gjester/priser-og-booking
https://grorudgk.no/gjester
https://gronmogk.no/gjestespill-2/
https://hafjellgolf.no/bane/booking-greenfee
https://hagagolf.no/gjest/booking-og-greenfee
https://aasgaardgolfpark.no/bane/priser-og-regler-2026
https://haldengk.no/start/gjest/greenfee
https://www.golfalpingolfbane.no/gjester/green-fee/
https://hammerfestgolf.no/index.php?pageID=123
https://hardangergolfklubb.no/prisar/
https://hgolf.no/vipps/'
https://haugalandgolf.no/bane/gjestespiller/
https://hauger-golfklubb.no/gjester
https://www.haugesundgolf.no/9hulls-bane-1
https://www.helgelandgolfklubb.no/klubben
https://hemsedalgolfklubb.no/banen/greenfee/
https://herdlagolfklubb.no/gjester/
https://www.hitragolf.no/priser/
https://hofgolfklubb.no/gjestespill/
https://hofgolfklubb.no/veien-til-golf/
https://holtsmarkgolf.no/banen/greenfee/
https://www.hovdengolf.com/priser
https://hhg.no/gjester/greenfee-og-starttid
https://hvalergk.no/gjester
https://www.jgk.no/greenfeebooking
https://www.karmoy-golfklubb.no/starte-med-golf/
https://kjekstad-gk.no/gjester/greenfee-og-booking
https://www.klabugolf.no/gjester
https://kongsberggolf.no/gjester/greenfee/https://kongsvingergolf.no/gjester/
https://kragk.no/gjest/greenfeepriser
https://kristiansandgk.no/gjester/priser
https://www.kristiansund-golfklubb.no/booking-greenfee
https://krokhol.no/gjester/greenfee
https://golfen.no/gjestespillere/
https://larvikgolf.no/gjester/greenfee-og-priser
https://lindesnesgolfklubb.no/banen/
https://www.lofotenlinks.no/golf/greenfee/
https://www.lommedalengk.no
https://losby.no/gjest/booking-greenfee
https://mandalgk.no/gjester/#greenfee-2026
https://www.midttromsgolf.no/next/p/35630/priser-og-apningstider
https://msgk.no/gjester
https://moldegolf.no/gjester/greenfee
https://www.evjegolf.no/gjester
https://morkgolf.no/gjester/greenfee-og-tidsbestilling
https://www.namdal-golfklubb.no/priser/
https://nesgolf.no/banen/gjestespill
https://nesfjellet.no/nb/golfbanen-ny
https://www.norgolf.com/greenfee/
https://nordhauggk.no/gjester/greenfee-priser
https://nordvegengolf.no/priser/
https://www.norefjell-golf.no/priser
https://golfparken.no/greenfee-for-gjester/#greenfeepriser
https://northcape-golfclub.no/gjest/greenfee
https://www.naroysundgolf.no/greenfee
https://notteroygolf.no/gjester/

153
backend/Medlemsskap.txt Normal file
View file

@ -0,0 +1,153 @@
https://ballerud.no/medlemmer
https://bamblegolfklubb.no/medlem/meldelemskontigent/
https://www.bgk.no/medlemmer
https://www.bjgk.no/om-oss/bli-medlem
https://bjaavanngk.no/medlem/innmeldingskjema
https://www.bodogolfklubb.no/klubben/medlemsskap-2024
https://borregb.no/medlemskap/medlemskategorier-og-priser
https://borregb.no/medlemskap/medlemskategorier-og-priser
https://www.bynesetgolf.no/medlemskap-og-fordeler/medlemskap-og-priser/
https://bmgk.no/klubben/medlemskap
https://drammengk.no/drammen-golfklubb/medlemskap
https://drobakgolf.no/medlemskap/medlemskap-og-priser
https://www.eikergolf.no/klubben/innmelding/
https://www.ekholtgolf.no/blimedlem
https://elverumgolf.no/medlemsskap/
https://fanagolf.no/medlemskap
https://www.fetgk.no/klubben/
https://www.florogolf.no/bli-medlem/
https://www.frostagk.no/next/p/70342/medlemskap
https://gfgk.no/bli-medlem/bli-medlem
https://giskegolf.no/medlemskap/
https://gjerdrumgolfklubb.no/medlemskap
https://gjersjoengolf.no/medlemskap
https://gtgk.no/medlemskap/medlemskap-og-priser
https://grenlandgolf.no/medlem/medlemskap-2019/
https://grimstadgolfklubb.no/medlemskap
https://grinigolfklubb.no/medlemskap/alle-medlemskap
https://grorudgk.no/medlemskap
https://gronmogk.no/bli-medlem/
https://hafjellgolf.no/medlemmer
https://hagagolf.no/medlem/medlemskap
https://aasgaardgolfpark.no/medlemsskap
https://haldengk.no/start/medlem
https://hallingdalgolfklubb.no/medlemskap/,https://www.golfalpingolfbane.no/gjester/green-fee/
https://hammerfestgolf.no/index.php?pageID=104
https://hardangergolfklubb.no/prisar/
https://hgolf.no/vtg-og-medlemsskap/
https://haugalandgolf.no/klubb/medlemskap/
https://hauger-golfklubb.no/medlemskap
https://www.haugesundgolf.no/menu-4
https://www.helgelandgolfklubb.no/klubben
https://hemsedalgolfklubb.no/medlemskap-oversikt/
https://herdlagolfklubb.no/innmelding/
https://hinnøy-gk.no/om-oss/medlemskapspillerett.html
https://www.hitragolf.no/priser/
https://www.hotellborge.no/husoy-golfklubb/
https://hofgolfklubb.no/medlemskap/
https://holtsmarkgolf.no/medlemskap-oversikt/
https://www.hovdengolf.com/medlem
https://hhg.no/medlemskap
https://hvalergk.no/medlemskap
https://www.imjelt.no/priser
https://www.jgk.no/kontingent
https://karasjokgolf.no/medlemskap/
https://www.karmoy-golfklubb.no/starte-med-golf/
https://kjekstad-gk.no/medlemskap
https://kongsberggolf.no/medlemskap/
https://kongsvingergolf.no/priser/
https://kragk.no/medlem
https://kristiansandgk.no/medlemskap
https://www.kristiansund-golfklubb.no/vtg-kurs
https://krokhol.no/medlemsservice/medlemskap
https://golfen.no/klubb/medlem/
https://larvikgolf.no/medlemskap
https://lilgk.no/medlemmer/innmelding
https://lindesnesgolfklubb.no/klubben/90-2/
https://www.lofotenlinks.no/medlemmer/
https://www.lommedalengk.no/priser
https://losby.no/medlemskap
https://legk.no/innmelding/
https://mandalgk.no/klubben/#kontingenter
https://melandgolf.no/medlemmer-1
https://www.midttromsgolf.no/next/p/52839/medlemsbetingelser
https://miklagardgolf.no/medlemskap/medlemskap-kampanje
https://msgk.no/medlemskap
https://moldegolf.no/medlemskap
https://www.evjegolf.no/bli-golfspiller-og-medlem/medlemskap-og-priser
https://morkgolf.no/medlemskap/medlemsavgifter
https://www.namdal-golfklubb.no/priser/
https://www.narvikgolf.no/bli-medlem/
https://nesgolf.no/medlemskap
https://www.norgolf.com/medlemskap/
https://nordhauggk.no/medlemskap/medlemskap
https://nordvegengolf.no/priser/
https://www.norefjell-golf.no/medlemskap/
https://golfparken.no/medlemskap/#senior
https://northcape-golfclub.no/medlemskap
https://www.naroysundgolf.no/medlem-old
https://notteroygolf.no/medlemskap/
https://www.minidrett.no/medlemskap/113698
https://ognagolf.no/bli-medlem/
https://onsoygolfklubb.no/medlemskap
https://www.oppdalgolfklubb.no/kontigent
https://oppegardgk.no/medlemskap/medlemskap-og-priser
https://www.oslogk.no/bli-medlem
https://occ.no/medlemskap/
https://www.polarsirkelen-golf.no/medlemsskap
https://www.prgk.no/bli-medlem
https://www.randaberggolf.no/blimedlem
https://www.randsfjordgolf.no/?p=1761
https://www.raumagolf.no/next/p/10216/kontakt
https://tonsberggolf.no/arsavgifter/
https://www.ringerikegk.no/homepage/medlemskap
https://rjukangolf.no/for-medlemmer/bli-medlem/
https://www.rorosgolf.no/bli-medlem/
https://bodogolfpark.com/medlemmer/#medlemskontingent
https://www.sandanegolf.no/klubben/bli-medlem/
https://sandegk.no/medlem/medlemskontingent
https://www.sandefjordgolf.no/klubben/bli-medlem/
https://www.sandnesgolfklubb.no/sandnes-golfklubb/bli-medlem-i-sandnes-golfklubb/
https://www.saudagolf.no/priser-og-medlemskap
https://www.seljegolfklubb.no/medlemskap/
https://sirdalfjellgolf.no/medlemskap/
https://www.skeigolf.no/skjema-for-innmelding-i-skei-golfklubb
https://skigk.no/klubb/medlemskap
https://skjeberggk.no/medlemskap/bli-medlem
https://slenesetgolf.no/bli-medlem/
https://smolagolfklubb.no/klubb.htm
https://solagk.no/klubben/medlemskap-og-priser
https://solagk.no/klubben/medlemskap-og-priser
https://www.solumgolf.club/medlem
https://soongolf.no/medlemmer/prisliste-medlemskap
https://sorknesgk.no/bli-medlem
https://sotragk.no/medlemskap/medlemskap-og-priser-2026
https://www.sgk.no/medlemskap/
https://steinkjer-golfklubb.no/bli-medlem/
https://www.stiklestad-golfklubb.no/medlemskap/
https://stjordal.golf/medlemskap
https://www.stordgolf.com/medlemskap
https://strandagolf.no/lokalmedlemskap/
https://www.sunndal-golfklubb.no/medlemskap/
https://sunnfjordgk.no/medlem/
https://sunnmoregolf.no/klubben/medlemskap#innmelding
https://surnadal-golfklubb.no/medlemskap/
https://tingvollgolf.no/priser/
https://tjomegolfklubb.no/bli-medlem/
https://tromsogolf.com/spill-banen/booking-greenfee/
https://golfklubben.no/medlem/kontigent-medlemskap
https://www.trondheimpar3golf.no
https://trysilgolf.no/start/medlem/medlemskap
https://tyrifjord-golfklubb.no/info/medlemmer/120/medlemskap-kategorier
https://www.tysnesgolf.no/medlem/
https://www.ugk.no/medlemsskap/
https://utsiktengolf.no/medlemskap
https://valdresgolf.no/om/bli-medlem/
https://www.vangolf.no/?page_id=541
https://varangergolf.no/varanger-golfklubb/medlemskontigent
https://www.vestgolf.com/medlemskap
https://voldagolf.no/index.php/om-klubben/medlemsskap
https://www.vossgolf.no/?p=335
https://vradalgolfklubb.no/medlemskap
https://ostmarkagolf.no/medlemskap
https://aalesundgk.no/medlemskap

81
backend/VtG.txt Normal file
View file

@ -0,0 +1,81 @@
https://www.altagolfklubb.no/banen/veien-til-golf/
https://arendalgk.no/kurs/veien-til-golf
https://askergolf.no/kurs/prov-golf-2026-vtg-nybegynnerkurs
https://askimgk.no/kurs/vtg-nybegynnerkurs
https://atlungstadgolf.no/pro/nybegynnerkurs/
https://romerikegk.no/collections/kurs-veien-til-golf
https://austraattgolf.no/?page_id=6449
https://ttmgolfacademy.com/ballerud/vtg-helg/
https://bamblegolfklubb.no/kurs/vtg-nybegynnerkurs/
https://www.bgk.no/veien-til-golf-kurs
https://www.bjgk.no/golfopplaering/vtg-kurs
https://bjaavanngk.no/trening/vtg-veien-til-golf
https://www.bodogolfklubb.no/kurs
https://borregb.no/trening-og-kurs/veien-til-golf-kurs
https://borregaardgk.no/aktuelt/veien-til-golf/veien-til-golf-pamelding/
https://www.bynesetgolf.no/kurs-og-trening/kursoversikt/
https://bmgk.no/kurs--trening/nybegynnerkurs-vtg
https://drammengk.no/trening/veien-til-golf
https://drobakgolf.no/kurs/nybegynnerkurs
https://www.eikergolf.no/golfkurs/kurs-2/
https://www.ekholtgolf.no/veien-til-golf
https://elverumgolf.no/medlemsskap/
https://fanagolf.no/nybegynner/pamelding-vtg-kurs,https://fanagolf.no/nybegynner/om-veien-til-golf
https://www.fetgk.no/kurs/
https://www.florogolf.no/kurs-vtg/
https://www.frostagk.no/next/p/71103/kurs
https://gfgk.no/klubben/begynne-med-golf
https://gjerdrumgolfklubb.no/kurs/veien-til-golf-nybegynnerkurs,https://gjerdrumgolfklubb.no/kurs/nybegynnerkurs-2026
https://gjersjoengolf.no/trening-og-kurs/nybegynnerkurs-vtg-2025
https://gtgk.no/kurs/pamelding-til-nybegynnerkurs
https://golfbutikken.no/golfskole/om-vtg-grenland-gk
https://grimstadgolfklubb.no/kurs
https://grorudgk.no/pro-kurs/nybegynnerkurs
https://gronmogk.no/veien-til-golf-kurs/
https://hafjellgolf.no/kurs-trening/veien-til-golf-kurs
https://hagagolf.no/proshop-trening/veien-til-golf-nybegynnerkurs
https://aasgaardgolfpark.no/kurs/vtg-veien-til-golf
https://haldengk.no/start/bli-golfspiller/veien-til-golf
https://hallingdalgolfklubb.no/veien-til-golf/
https://hammerfestgolf.no/index.php?artID=559&navB=1
https://hgolf.no/vtg-og-medlemsskap/
https://haugalandgolf.no/pga-pro/kurs-2024/
https://hauger-golfklubb.no/pro-kurs/vtg-nybegynnerkurs
https://www.haugesundgolf.no/nybegynnerkurs
https://hemsedalgolfklubb.no/veien-til-golf/
https://herdlagolfklubb.no/vtg/
https://www.hitragolf.no/l/veien-til-golf/
https://holtsmarkgolf.no/nybegynnerkurs-golf/
https://hhg.no/pro-kurs-og-trening/vtg-kurs-for-nybegynnere
https://hvalergk.no/kurs-instruksjon/veien-til-golf-nybegynnerkurs
https://www.jgk.no/golfkursvtg
https://www.karmoy-golfklubb.no/golfkurs/
https://kjekstad-gk.no/instruksjon-og-kurs/nybegynnerkurs
https://kongsberggolf.no/veien-til-golf/pamelding-til-veien-til-golf/,https://kongsberggolf.no/veien-til-golf/
https://kongsvingergolf.no/kurs/#intro
https://kragk.no/pro,https://golfbutikken.no/golfskole/om-vtg-kragero-gk,https://golfbutikken.no/golfskole/program-kragero-golfklubb
https://kristiansandgk.no/kurs/nybegynnerkurs
https://www.kristiansund-golfklubb.no/vtg-kurs
https://krokhol.no/instruksjon-og-trening/veien-til-golf
https://golfen.no/veien-til-golf/
https://larvikgolf.no/pro-og-kurs/nybegynnerkurs-vtg
https://lilgk.no/kurs/nybegynnerkurs-2025
https://www.lofotenlinks.no/veien-til-golf-kurs/
https://losby.no/nybegynner/veien-til-golf
https://mandalgk.no/bli-medlem/#veien-til-golf-vtg-nybegynnerkurs
https://melandgolf.no/om-gjestespill
https://www.midttromsgolf.no/next/p/87550/gavekort-vtg-kurs,https://www.midttromsgolf.no/next/p/35531/pamelding-vtg
https://miklagardgolf.no/trening-kurs/vtg-veien-til-golf
https://msgk.no/kurs/nybegynnerkurs
https://www.evjegolf.no/bli-golfspiller-og-medlem/veien-til-golf-og-kurs
https://morkgolf.no/instruksjon-kurs/veien-til-golf-nybegynnerkurs
https://www.namdal-golfklubb.no/kurs/
https://www.narvikgolf.no/kurs-trening/
https://nesgolf.no/kurs-og-trening/nybegynnerkurs-vtg
https://nesfjellet.no/nb/golfklubben
https://www.norgolf.com/519-2/
https://nordvegengolf.no/golfkurs-2/
https://www.norefjell-golf.no/klubben/aktuelt/veien-til-golf-kurs
https://golfparken.no/veien-til-golf/
https://northcape-golfclub.no/veien-til-golf-vtg-kurs
https://notteroygolf.no/kurs-treninger/nybegynnerkurs/

101
backend/import_urls.py Normal file
View file

@ -0,0 +1,101 @@
"""
TEE OFF - AUTOMATISK URL-IMPORTØR
---------------------------------------------------------------------------
Leser tekstfiler med lenker og forsøker å matche dem mot eksisterende
golfanlegg i databasen basert domenenavn.
---------------------------------------------------------------------------
"""
import asyncio
import asyncpg
import os
from urllib.parse import urlparse
DB_URL = os.getenv("DATABASE_URL", "postgresql://teeoff_admin:teeoff_secret_password@db:5432/teeoff")
# Hvilke filer vi skal lese, og hvilke databasefelt de tilhører
FILES_TO_IMPORT = {
"Medlemsskap.txt": "medlemskap_url",
"GreenFee.txt": "greenfee_url",
"VtG.txt": "vtg_lenke"
}
def extract_domain(url: str) -> str:
"""Henter ut hoveddomenet (f.eks. 'tyrifjord-golfklubb.no') fra en URL."""
try:
domain = urlparse(url.strip()).netloc.lower()
if domain.startswith("www."):
domain = domain[4:]
return domain
except:
return ""
async def run_import():
print("🚀 Starter URL-importør...")
conn = await asyncpg.connect(DB_URL)
try:
# Hent alle eksisterende anlegg og deres domener
facilities = await conn.fetch("SELECT id, name, website_url, scrape_status_url FROM facilities")
# Bygg en ordbok: { 'domenenavn.no': facility_id } for superraskt oppslag
domain_map = {}
for f in facilities:
# Prøv å hente domene fra website_url
if f['website_url']:
domain = extract_domain(f['website_url'])
if domain: domain_map[domain] = f['id']
# Prøv også scrape_status_url for sikkerhets skyld
if f['scrape_status_url']:
domain = extract_domain(f['scrape_status_url'])
if domain: domain_map[domain] = f['id']
print(f"📋 Fant {len(domain_map)} unike domener i databasen.")
# Gå gjennom fil for fil
for filename, db_field in FILES_TO_IMPORT.items():
print(f"\n▶️ BEHANDLER: {filename} -> Setter felt: {db_field}")
if not os.path.exists(filename):
print(f" ⚠️ Filen '{filename}' ble ikke funnet. Hopper over.")
continue
with open(filename, 'r', encoding='utf-8') as file:
lines = [line.strip() for line in file.readlines() if line.strip()]
matched_count = 0
unmatched = []
for line in lines:
# Hvis det er flere URL-er på samme linje separert med komma,
# matcher vi basert på den FØRSTE URL-en.
first_url = line.split(',')[0].strip()
domain = extract_domain(first_url)
# Hvis vi fant en match i databasen!
if domain in domain_map:
fac_id = domain_map[domain]
# Oppdater databasen med HELE linjen (for å bevare ev. komma-lenker)
await conn.execute(f"""
UPDATE facilities
SET {db_field} = $1
WHERE id = $2
""", line, fac_id)
matched_count += 1
else:
unmatched.append(line)
print(f" ✅ Matchet og oppdatert {matched_count} anlegg.")
if unmatched:
print(f" ❌ Følgende {len(unmatched)} URL-er fant ingen match i databasen og må legges inn manuelt:")
for url in unmatched:
print(f" - {url}")
finally:
await conn.close()
print("\n🏁 Import fullført!")
if __name__ == "__main__":
asyncio.run(run_import())

View file

@ -98,7 +98,7 @@ def format_row(row):
json_list_fields = [
'course_statuses', 'courses', 'gallery', 'greenfee',
'faqs', 'shotzoom', 'social_links', 'holes', 'golfpakker', 'cooperating_clubs'
'faqs', 'shotzoom', 'social_links', 'holes', 'golfpakker', 'cooperating_clubs', 'vtg_datoer'
]
json_dict_fields = [
'amenities', 'vtg', 'nsg_data', 'golfamore_data', 'membership_draft'

View file

@ -1,12 +1,14 @@
"use client";
/**
* TEE OFF DETAIL VIEW - COMPLETE v3.22
* TEE OFF DETAIL VIEW - COMPLETE v3.4 (FINAL LAYOUT FIX)
* ---------------------------------------------------------------------------
* 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).
* NEW: Sosiale Medier, Footnote og Samarbeidende klubber integrert.
* NEW: Priser (Medlemskap + Greenfee) i 2-kolonne Grid (xl:grid-cols-2).
* NEW: Veien til Golf (VTG) i full bredde under prisene, med robust array-parsing.
* REGEL: Beholder monokrome ikoner, 22/78 layout og robust JSON-parsing.
* ---------------------------------------------------------------------------
*/
@ -71,6 +73,7 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
const [showBackToTop, setShowBackToTop] = useState(false);
const [currentSlide, setCurrentSlide] = useState(0);
// Robust parser for å hente ut JSONB data fra Postgres trygt
const parseJson = (val: any, fallback: any) => {
if (!val) return fallback;
if (typeof val === 'object') return val;
@ -82,9 +85,12 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
const amenities = parseJson(facility.amenities, {});
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, []);
// Pris og kurs-arrays
const greenfeeRaw = parseJson(facility.greenfee, []);
const vtgDatoer = parseJson(facility.vtg_datoer, []);
const golfamoreData = parseJson(facility.golfamore_data, {});
const nsgData = parseJson(facility.nsg_data, {});
const socialLinksRaw = parseJson(facility.social_links, []);
@ -96,13 +102,6 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
const hasGolfamore = facility.golfamore === true;
const hasNSG = facility.nsg_url || (nsgData && Object.keys(nsgData).length > 0);
const groupedGreenfee: Record<string, any[]> = greenfeeRaw.reduce((acc: any, curr: any) => {
const bane = curr.banenavn || "Gjestespill";
if (!acc[bane]) acc[bane] = [];
acc[bane].push(curr);
return acc;
}, {});
const sidebarLinkClass = "flex items-center gap-4 text-[#11280f] hover:text-[#ff5722] transition-colors group";
const resourceBtnClass = "flex justify-between items-center p-5 bg-gray-50 rounded-2xl text-[11px] font-black uppercase text-[#11280f] hover:bg-[#ff5722] hover:text-white transition-all group";
@ -157,7 +156,7 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
{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-[#ff5722] 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-[#ff5722] 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-[#ff5722] 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-[#ff5722] hover:text-white transition-all"><Icon children={ICONS.pin} /></a>
<a href={`https://www.google.com/maps/search/?api=1&query=$?q=${facility.lat},${facility.lng}`} target="_blank" className="w-9 h-9 bg-white rounded-xl flex items-center justify-center hover:bg-[#ff5722] 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-[#ff5722] hover:text-white transition-all"><Icon children={ICONS.weather} /></a>}
</div>
@ -210,7 +209,7 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
<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"}>
<a href={`https://www.google.com/maps/search/?api=1&query=$?q=${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-[#ff5722] transition-colors">{facility.address}<br/>{facility.city}</span>
</a>
</div>
@ -221,7 +220,6 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
<div className="pt-6 border-t border-gray-50 mt-6 flex flex-wrap gap-3">
{socialLinks.map((social: any, idx: number) => {
const platform = (social.platform || '').toLowerCase().trim();
// Finn riktig ikon, fall tilbake til en generell link-pil
const iconData = SOCIAL_ICONS[platform] || <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3" />;
return (
@ -346,7 +344,7 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
<section id="map" className="space-y-6">
<h2 className="text-3xl md:text-4xl font-black uppercase tracking-tighter flex items-center gap-5 ml-6 md:ml-0">Kart <span className="h-1 flex-grow bg-gray-100 rounded-full" /></h2>
<div className="w-full md:rounded-[3rem] overflow-hidden shadow-xl h-[450px] md:h-[650px] border-y-4 md:border-[12px] border-white bg-gray-100">
<iframe width="100%" height="100%" style={{ border: 0 }} src={`https://maps.google.com/maps?q=${facility.lat},${facility.lng}&t=k&z=15&ie=UTF8&iwloc=&output=embed`} allowFullScreen />
<iframe width="100%" height="100%" style={{ border: 0 }} src={`https://www.google.com/maps/search/?api=1&query=$?q=${facility.lat},${facility.lng}&t=k&z=15&ie=UTF8&iwloc=&output=embed`} allowFullScreen />
</div>
</section>
@ -360,58 +358,168 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
</section>
)}
{/* 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>
<div className="space-y-10">
{Object.keys(groupedGreenfee).length > 0 ? (
Object.entries(groupedGreenfee).map(([bane, priser], idx) => (
<div key={idx} className="space-y-4">
{!(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>
)}
<div className="space-y-2">
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest">Voksne</p>
{priser.map((g, i) => (
<div key={i} className="flex justify-between py-2 border-b border-gray-50/50 text-sm font-bold">
<span className="text-gray-500">{g.priskategori}</span>
<span>kr {g.pris_voksne || '--'},-</span>
</div>
))}
</div>
{priser.some(g => g.pris_junior) && (
<div className="space-y-2 pt-4">
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest">Junior</p>
{priser.map((g, i) => (
<div key={i} className="flex justify-between py-2 border-b border-gray-50/50 text-sm font-bold">
<span className="text-gray-500">{g.priskategori}</span>
<span>kr {g.pris_junior || '--'},-</span>
</div>
))}
</div>
)}
</div>
))
) : <p className="text-gray-400 italic py-6">Ingen priser funnet.</p>}
</div>
<p className="mt-10 text-[10px] text-gray-300 font-black uppercase tracking-widest italic">Krav: {facility.guest_requirements || 'Klubbhandicap'}</p>
</div>
{/* 8. PRISER (MEDLEMSKAP, GREENFEE & VTG) */}
<section id="prices" className="pt-10">
<h2 className="text-3xl md:text-4xl font-black uppercase tracking-tighter flex items-center gap-5 ml-6 md:ml-0 mb-8">
Priser <span className="h-1 flex-grow bg-gray-100 rounded-full" />
</h2>
<div className="bg-white p-10 md:p-14 md:rounded-[3rem] shadow-sm flex flex-col justify-between">
<div>
<h3 className="text-2xl font-black mb-10 uppercase tracking-tighter">Medlemskap</h3>
<div className="bg-[#f1f7ed] p-12 md:rounded-[2.5rem] mb-8 text-center border border-[#7ca982]/10">
<p className="text-[#7ca982] text-[11px] font-black uppercase mb-3 tracking-widest">{facility.navn_standard_medlemskap || "Standard"}</p>
<p className="text-6xl font-black text-[#11280f]">kr {facility.standard_medlemskap || '--'},-</p>
{facility.standard_medlemskap_kommentarer && <p className="text-[10px] text-gray-400 mt-4 uppercase font-bold italic leading-tight whitespace-pre-wrap">{facility.standard_medlemskap_kommentarer}</p>}
</div>
{facility.navn_rimeligste_alternativ && (
<div className="px-8 py-5 bg-gray-50 rounded-2xl border border-gray-100 flex justify-between items-center text-sm font-bold"><span className="text-gray-400 uppercase text-[10px] tracking-widest">{facility.navn_rimeligste_alternativ}</span><span className="text-[#11280f]">kr {facility.rimeligste_alternativ},-</span></div>
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6 lg:gap-8 items-start">
{/* VENSTRE KOLONNE: MEDLEMSKAP */}
{(facility.standard_medlemskap || facility.rimeligste_alternativ) && (
<div className="bg-white border-2 border-[#11280f]/5 rounded-3xl p-6 lg:p-8 shadow-sm h-full flex flex-col">
<div className="flex justify-between items-center mb-6">
<h3 className="text-xl font-black text-[#11280f] uppercase tracking-tighter flex items-center gap-2">
<span></span> Medlemskap
</h3>
{facility.medlemskap_url && (
<a href={facility.medlemskap_url.split(',')[0].trim()} target="_blank" className="text-[10px] font-black uppercase tracking-widest text-[#7ca982] hover:text-[#11280f] transition-colors">
Se alle
</a>
)}
</div>
<div className="space-y-6 flex-grow">
{facility.standard_medlemskap && (
<div className="bg-gray-50 rounded-2xl p-5 border border-gray-100 relative overflow-hidden group hover:border-[#8bc34a]/30 transition-colors">
<div className="absolute top-0 right-0 bg-[#8bc34a] text-white text-[9px] font-black uppercase tracking-widest px-3 py-1 rounded-bl-xl">Mest valgte</div>
<span className="block text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1">Standard</span>
<span className="text-3xl font-black text-[#11280f]">{facility.standard_medlemskap},-</span>
{facility.standard_medlemskap_navn && <p className="text-xs font-bold mt-1 text-[#8bc34a]">{facility.standard_medlemskap_navn}</p>}
{facility.standard_medlemskap_kommentarer && (
<p className="text-[10px] text-gray-500 mt-2 uppercase font-bold italic leading-tight border-t border-gray-200 pt-2">
{facility.standard_medlemskap_kommentarer.split('\n').map((line: string, i: number) => (
<span key={i}>{line}<br /></span>
))}
</p>
)}
</div>
)}
{facility.rimeligste_alternativ && (
<div className="bg-white rounded-2xl p-5 border border-gray-100 hover:border-[#8bc34a]/30 transition-colors">
<span className="block text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1">Rimeligste golfkort</span>
<span className="text-xl font-black text-gray-600">{facility.rimeligste_alternativ},-</span>
{facility.rimeligste_navn && <p className="text-[10px] font-bold mt-1 text-gray-500">{facility.rimeligste_navn}</p>}
</div>
)}
</div>
</div>
)}
</div>
<a href={facility.medlemskap_url ? facility.medlemskap_url.split(',')[0].trim() : '#'} target="_blank" className="mt-10 block w-full text-center bg-[#11280f] text-white p-7 rounded-2xl font-black uppercase text-xs tracking-widest shadow-xl hover:bg-black transition-all">Se alle alternativer</a>
</div>
{/* HØYRE KOLONNE: GREENFEE */}
{greenfeeRaw && greenfeeRaw.length > 0 && (
<div className="bg-white border-2 border-[#11280f]/5 rounded-3xl p-6 lg:p-8 shadow-sm h-full flex flex-col">
<div className="flex justify-between items-center mb-6">
<h3 className="text-xl font-black text-[#11280f] uppercase tracking-tighter flex items-center gap-2">
<span>🎫</span> Greenfee
</h3>
{facility.greenfee_url && (
<a href={facility.greenfee_url.split(',')[0].trim()} target="_blank" className="text-[10px] font-black uppercase tracking-widest text-[#7ca982] hover:text-[#11280f] transition-colors">
Alle priser
</a>
)}
</div>
<div className="overflow-x-auto hide-scrollbar flex-grow">
<table className="w-full text-left min-w-[300px]">
<thead>
<tr className="border-b-2 border-gray-100 text-[10px] font-black uppercase tracking-widest text-gray-400">
<th className="pb-3 pr-2">Bane/Kat.</th>
<th className="pb-3 text-right pr-2">Voksen</th>
<th className="pb-3 text-right">Junior</th>
</tr>
</thead>
<tbody className="text-xs font-bold text-[#11280f]">
{greenfeeRaw.map((gf: any, idx: number) => (
<tr key={idx} className="border-b border-gray-50 hover:bg-gray-50 transition-colors">
<td className="py-3 pr-2 leading-tight">
<span className="block truncate max-w-[150px] sm:max-w-[200px]" title={gf.banenavn}>{gf.banenavn}</span>
<span className="text-[9px] text-gray-400 uppercase tracking-widest block truncate max-w-[150px] sm:max-w-[200px]" title={gf.priskategori}>{gf.priskategori}</span>
</td>
<td className="py-3 text-right pr-2 text-[#8bc34a] font-black text-sm whitespace-nowrap">
{gf.pris_voksne ? `${gf.pris_voksne},-` : '-'}
</td>
<td className="py-3 text-right text-blue-400 whitespace-nowrap">
{gf.pris_junior ? `${gf.pris_junior},-` : '-'}
</td>
</tr>
))}
</tbody>
</table>
</div>
{facility.guest_requirements && (
<p className="mt-4 text-[10px] text-gray-400 font-black uppercase tracking-widest italic pt-4 border-t border-gray-50">
Krav: {facility.guest_requirements}
</p>
)}
</div>
)}
</div>
{/* VEIEN TIL GOLF (VTG) - FULL BREDDE UNDER */}
{(facility.vtg_pris || facility.vtg_beskrivelse || (vtgDatoer && vtgDatoer.length > 0)) && (
<div className="mt-6 lg:mt-8 bg-[#8bc34a] text-white rounded-3xl p-6 lg:p-10 shadow-lg relative overflow-hidden group">
{/* Bakgrunnseffekt */}
<div className="absolute -right-20 -top-20 opacity-10 text-[200px] pointer-events-none transform group-hover:scale-110 transition-transform duration-700">🏌</div>
<div className="relative z-10">
<h3 className="text-2xl font-black uppercase tracking-tighter mb-4 flex items-center gap-3">
Nybegynnerkurs (Veien til Golf)
</h3>
{facility.vtg_beskrivelse && (
<p className="text-sm md:text-base text-white/90 mb-8 leading-relaxed font-medium max-w-4xl">
{facility.vtg_beskrivelse}
</p>
)}
<div className="flex flex-col lg:flex-row gap-6 items-start lg:items-center justify-between bg-white/10 rounded-2xl p-6 backdrop-blur-sm border border-white/20">
{/* Pris */}
{facility.vtg_pris && (
<div className="flex-shrink-0">
<span className="block text-[10px] font-black uppercase tracking-widest text-white/70 mb-1">Standard voksenpris</span>
<span className="text-4xl font-black">{facility.vtg_pris},-</span>
</div>
)}
{/* Datoer */}
{vtgDatoer && vtgDatoer.length > 0 && (
<div className="flex-grow w-full lg:w-auto lg:px-6">
<h4 className="text-[10px] font-black uppercase tracking-widest text-white/70 mb-3">Kommende kurs:</h4>
<div className="flex flex-wrap gap-2">
{vtgDatoer.map((kurs: any, i: number) => {
const status = (kurs.status || '').toLowerCase();
const isFull = status.includes('full');
const isWaitlist = status.includes('vente') || status.includes('få');
let badgeColor = "bg-white/20 text-white";
if (isFull) badgeColor = "bg-red-500/80 text-white line-through opacity-75";
if (isWaitlist) badgeColor = "bg-yellow-400 text-[#11280f]";
return (
<div key={i} className={`flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-bold border border-white/10 ${badgeColor}`}>
<span>{kurs.dato}</span>
<span className="text-[8px] uppercase tracking-widest opacity-80 border-l border-white/20 pl-2 ml-1">{kurs.status}</span>
</div>
);
})}
</div>
</div>
)}
{/* Påmeldingsknapp */}
{facility.vtg_lenke && (
<a href={facility.vtg_lenke.split(',')[0].trim()} target="_blank" rel="noopener noreferrer" className="mt-4 lg:mt-0 w-full lg:w-auto text-center inline-block bg-white text-[#8bc34a] px-8 py-4 rounded-xl text-xs font-black uppercase tracking-widest hover:bg-[#11280f] hover:text-white hover:scale-105 transition-all shadow-xl flex-shrink-0">
Påmelding
</a>
)}
</div>
</div>
</div>
)}
</section>
{/* 9. SCOREKORT SEKSJON */}