diff --git a/backend/__pycache__/main.cpython-311.pyc b/backend/__pycache__/main.cpython-311.pyc index 81e3fbe..105c763 100644 Binary files a/backend/__pycache__/main.cpython-311.pyc and b/backend/__pycache__/main.cpython-311.pyc differ diff --git a/backend/sync_greenfee.py b/backend/sync_greenfee.py new file mode 100644 index 0000000..745a80e --- /dev/null +++ b/backend/sync_greenfee.py @@ -0,0 +1,79 @@ +import asyncio, asyncpg, urllib.request, json + +DB_URL = "postgresql://teeoff_admin:teeoff_secret_password@db:5432/teeoff" +# Vi fjerner acf_format=standard da rå-feltnavnene er tryggere her +WP_API_URL = "https://teeoff.no/wp-json/wp/v2/golfbaner?per_page=100" + +def decode_html(text): + if not text: return "" + return str(text).replace('&', '&').replace('&', '&').replace(' ', ' ').strip() + +async def run_greenfee_sync(): + print("🎯 Starter GREENFEE-SYNC v1.2 (Basert på rå-API mapping)...") + conn = await asyncpg.connect(DB_URL) + page = 1 + total_updated = 0 + + while True: + try: + req = urllib.request.Request(f"{WP_API_URL}&page={page}", headers={'User-Agent': 'TeeOff-Sync'}) + with urllib.request.urlopen(req) as response: + data = json.loads(response.read().decode()) + except: break + if not data: break + + for post in data: + slug = post['slug'] + acf = post.get('acf', {}) + + # Henter banenavn for å gruppere riktig + bane_1_navn = acf.get('navn_pa_hovedbane') or "Hovedbanen" + bane_2_navn = acf.get('navn_pa_sekundar_bane') or "Bane 2" + + final_greenfee = [] + + # --- MAPPER BANE 1 (Voksne + Junior) --- + voksne_1 = acf.get('greenfee_-_voksne') or [] + junior_1 = acf.get('greenfee_-_junior') or [] + + for i, item in enumerate(voksne_1): + row = { + "banenavn": bane_1_navn, + "priskategori": item.get('priskategori'), + "pris_voksne": item.get('pris_voksne') + } + # Legger til juniorpris hvis den finnes på samme index + if i < len(junior_1): + row["pris_junior"] = junior_1[i].get('pris_junior') + final_greenfee.append(row) + + # --- MAPPER BANE 2 (Voksne + Junior) --- + voksne_2 = acf.get('greenfee_-_voksne_bane_to') or [] + junior_2 = acf.get('greenfee_-_junior_bane_to') or [] + + for i, item in enumerate(voksne_2): + row = { + "banenavn": bane_2_navn, + "priskategori": item.get('priskategori_bane_to'), + "pris_voksne": item.get('pris_voksne_bane_to') + } + if i < len(junior_2): + row["pris_junior"] = junior_2[i].get('pris_junior_bane_to') + final_greenfee.append(row) + + # Henter krav (Gjeste_krav) + reqs = decode_html(acf.get('krav_til_gjestespillere')) + + if final_greenfee: + await conn.execute(''' + UPDATE facilities SET greenfee = $1::jsonb, guest_requirements = $2 WHERE slug = $3 + ''', json.dumps(final_greenfee), reqs, slug) + print(f"✅ {slug}: Importerte {len(final_greenfee)} prisrader for {bane_1_navn}/{bane_2_navn}") + total_updated += 1 + + page += 1 + await conn.close() + print(f"\n✨ Ferdig! Oppdaterte priser for {total_updated} anlegg.") + +if __name__ == "__main__": + asyncio.run(run_greenfee_sync()) \ No newline at end of file diff --git a/frontend/src/app/golfbaner/[slug]/FacilityDetailView.tsx b/frontend/src/app/golfbaner/[slug]/FacilityDetailView.tsx index 88b4a0a..3d17902 100644 --- a/frontend/src/app/golfbaner/[slug]/FacilityDetailView.tsx +++ b/frontend/src/app/golfbaner/[slug]/FacilityDetailView.tsx @@ -1,12 +1,11 @@ "use client"; /** - * TEE OFF DETAIL VIEW - COMPLETE v3.15 (FINAL POLISH) + * TEE OFF DETAIL VIEW - COMPLETE v3.18 * --------------------------------------------------------------------------- - * FIX: Flyttet Turneringer til over Baneguide i ressurslisten. - * FIX: Endret "Statistik" til "Statistikk". - * FIX: Ensrettet alle knapper i "Andre ressurser" (samme bakgrunn og hover). - * FIX: Sørget for at alle felt med lenker i "Andre tilbud" er oransje (#ff5722). - * REGEL: Beholder monokrome ikoner, 22/78 layout og robust JSON-parsing. + * 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 på telefon. * --------------------------------------------------------------------------- */ @@ -57,9 +56,17 @@ export default function FacilityDetailView({ facility }: { facility: any }) { 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 greenfee = parseJson(facility.greenfee, []); + const greenfeeRaw = parseJson(facility.greenfee, []); const shotzoom = parseJson(facility.shotzoom, []); - + + // Grupper priser etter bane (f.eks Østmork, Vestmork) + const groupedGreenfee: Record = greenfeeRaw.reduce((acc: any, curr: any) => { + const bane = curr.banenavn || "Gjestespill"; + if (!acc[bane]) acc[bane] = []; + acc[bane].push(curr); + 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"; @@ -148,8 +155,6 @@ export default function FacilityDetailView({ facility }: { facility: any }) { {/* 4. 3-KOLONNE INFO */}
- - {/* KOLONNE 1: ANDRE RESSURSER (Ensrettet uttrykk & Sortering) */}

Andre Ressurser

@@ -175,13 +180,11 @@ export default function FacilityDetailView({ facility }: { facility: any }) { )} {shotzoom.map((sz: any, i: number) => ( - Statistikk: {sz.shotzoom_beskrivelse?.replace(/ /g, ' ').toUpperCase()} + Statistikk: {sz.shotzoom_beskrivelse?.replace(/ ?/g, ' ').trim().toUpperCase()} ))}
- - {/* KOLONNE 2: BANEN */}

Banen

@@ -193,8 +196,6 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
Arkitekt:{facility.architect || '--'}
- - {/* KOLONNE 3: ANDRE TILBUD (Oransje lenker) */}

Andre Tilbud

@@ -235,26 +236,58 @@ export default function FacilityDetailView({ facility }: { facility: any }) {
)} - {/* 8. PRISER SEKSJON */} + {/* 8. PRISER & MEDLEMSKAP (IKONER FJERNET) */}
-
-

⛳ Gjestespill

-
- {greenfee.length > 0 ? ( - greenfee.map((g: any, i: number) => ( -
{g.priskategori}kr {g.pris_voksne || '--'},-
+
+

Gjestespill

+
+ {Object.keys(groupedGreenfee).length > 0 ? ( + Object.entries(groupedGreenfee).map(([bane, priser], idx) => ( +
+ {/* Skjul baneheader hvis den bare gjentar "Gjestespill" og det bare er én gruppe */} + {!(bane === "Gjestespill" && Object.keys(groupedGreenfee).length === 1) && ( +

{bane}

+ )} +
+

Voksne

+ {priser.map((g, i) => ( +
+ {g.priskategori} + kr {g.pris_voksne || '--'},- +
+ ))} +
+ {priser.some(g => g.pris_junior) && ( +
+

Junior

+ {priser.map((g, i) => ( +
+ {g.priskategori} + kr {g.pris_junior || '--'},- +
+ ))} +
+ )} +
)) ) :

Ingen priser funnet.

}
+

Krav: {facility.guest_requirements || 'Klubbhandicap'}

-
+ +
-

💛 Medlemskap

+

Medlemskap

+

{facility.navn_standard_medlemskap || "Standard"}

kr {facility.standard_medlemskap || '--'},-

+ {facility.standard_medlemskap_kommentarer &&

{facility.standard_medlemskap_kommentarer}

}
+ {facility.navn_rimeligste_alternativ && ( +
{facility.navn_rimeligste_alternativ}kr {facility.rimeligste_alternativ},-
+ )}
- Se alle alternativer + Se alle alternativer
@@ -271,7 +304,9 @@ export default function FacilityDetailView({ facility }: { facility: any }) { - {showBackToTop && ( )} + {showBackToTop && ( + + )} ); } \ No newline at end of file