diff --git a/backend/main.py b/backend/main.py index 227f35c..ca84ae9 100644 --- a/backend/main.py +++ b/backend/main.py @@ -2283,10 +2283,113 @@ async def upsert_facility_rating(request: Request, payload: FacilityRatingUpsert # --- DATA ENDPOINTS --- @app.get("/api/facilities") -async def get_facilities(): +async def get_facilities(summary: bool = False): """Henter alle golfanlegg med aggregert banestatus for forsiden.""" async with app.state.pool.acquire() as conn: - rows = await conn.fetch(""" + if summary: + rows = await conn.fetch(""" + SELECT + f.id, + f.slug, + f.name, + f.architect, + f.description, + f.city, + f.county, + f.banetype, + f.image_url, + f.phone, + f.website_url, + f.golfbox_booking_url, + f.golfbox_tournament_url, + f.weather_url, + f.lat, + f.lng, + f.golfamore, + f.golfamore_url, + f.nsg_url, + f.greenfee, + f.standard_medlemskap, + f.vtg_pris, + f.vtg_lenke, + f.vtg_beskrivelse, + f.amenities, + f.golfamore_data, + f.nsg_data, + f.vtg_datoer, + f.footnote, + f.footnote_updated_at, + f.status_updated_at, + ( + SELECT jsonb_agg(cs) FROM ( + SELECT id, name, status FROM courses + WHERE facility_id = f.id AND status != 'finnes_ingen_bane_to' + ORDER BY is_main_course DESC, id ASC + ) cs + ) as course_statuses, + ( + SELECT COUNT(*) + FROM holes h + JOIN courses c ON c.id = h.course_id + WHERE c.facility_id = f.id + ) as total_hole_count, + ( + SELECT jsonb_build_object( + '3', COUNT(*) FILTER (WHERE h.par = 3), + '4', COUNT(*) FILTER (WHERE h.par = 4), + '5', COUNT(*) FILTER (WHERE h.par = 5), + '6', COUNT(*) FILTER (WHERE h.par = 6) + ) + FROM holes h + JOIN courses c ON c.id = h.course_id + WHERE c.facility_id = f.id + ) as hole_par_counts, + ( + SELECT MIN((length_value.value)::int) + FROM holes h + JOIN courses c ON c.id = h.course_id + CROSS JOIN LATERAL jsonb_each_text(COALESCE(h.lengths, '{}'::jsonb)) AS length_value(key, value) + WHERE c.facility_id = f.id + AND length_value.key IN ('kortest', 'kort', 'mellomkort', 'mellomlang', 'lang', 'lengst') + AND length_value.value ~ '^[0-9]+$' + AND (length_value.value)::int BETWEEN 30 AND 900 + ) as shortest_hole_meters, + ( + SELECT MAX((length_value.value)::int) + FROM holes h + JOIN courses c ON c.id = h.course_id + CROSS JOIN LATERAL jsonb_each_text(COALESCE(h.lengths, '{}'::jsonb)) AS length_value(key, value) + WHERE c.facility_id = f.id + AND length_value.key IN ('kortest', 'kort', 'mellomkort', 'mellomlang', 'lang', 'lengst') + AND length_value.value ~ '^[0-9]+$' + AND (length_value.value)::int BETWEEN 30 AND 900 + ) as longest_hole_meters, + ( + SELECT jsonb_agg(w_data ORDER BY w_data.day_offset ASC) FROM ( + SELECT + forecast_date, + day_offset, + dry_all_day, + dry_daylight, + precip_mm, + precip_probability_max, + daylight_precip_mm, + daylight_precip_probability_max, + confidence, + source_updated_at, + source_expires_at, + calculated_at + FROM facility_weather_forecast + WHERE facility_id = f.id + ORDER BY day_offset ASC + ) w_data + ) as weather_forecast + FROM facilities f + WHERE COALESCE(f.is_published, TRUE) = TRUE + ORDER BY f.name ASC + """) + else: + rows = await conn.fetch(""" SELECT f.*, ( SELECT jsonb_agg(cs) FROM ( SELECT id, name, status FROM courses diff --git a/frontend/src/app/facilityData.ts b/frontend/src/app/facilityData.ts index 4f87fbb..c7d2294 100755 --- a/frontend/src/app/facilityData.ts +++ b/frontend/src/app/facilityData.ts @@ -9,6 +9,7 @@ export type FacilityRecord = { id: number; slug: string; name: string; + architect?: string | null; description?: string | null; city?: string | null; county?: string | null; diff --git a/frontend/src/app/sted/[slug]/PlaceExplorer.tsx b/frontend/src/app/sted/[slug]/PlaceExplorer.tsx index 114190a..240239a 100644 --- a/frontend/src/app/sted/[slug]/PlaceExplorer.tsx +++ b/frontend/src/app/sted/[slug]/PlaceExplorer.tsx @@ -1,18 +1,25 @@ "use client"; -import { useEffect, useMemo, useState } from "react"; +import dynamic from "next/dynamic"; +import { useEffect, useState } from "react"; import FacilitySearch from "@/app/FacilitySearch"; -import PlaceMap from "@/components/PlaceMap"; import { type EnrichedFacility, - type FacilityRecord, - enrichFacilities, - filterFacilitiesByArea, getPlacePreposition, } from "@/app/facilityData"; +const PlaceMap = dynamic(() => import("@/components/PlaceMap"), { + loading: () => ( +