import type { ApiCourseHole, ApiCourseTee } from "@/components/course-template-editor" import type { Account, ApiGender, Course, Facility, Gender, KnownGuest, Tee } from "./types" // --- Kjønn-konvertering (samme mønster som new-round.tsx) ------------------ export function apiGenderOf(g: Gender): ApiGender | "x" { return g === "mann" ? "m" : g === "kvinne" ? "f" : "x" } export function uiGenderOf(g: ApiGender): Gender { return g === "m" ? "mann" : "kvinne" } // --- Rå API-typer (kun det denne veiviseren faktisk leser) ----------------- type ApiTeeOption = { name: string; genders: ApiGender[] } type ApiFacility = { slug: string; name: string; city: string | null; county: string | null } type ApiNearbyFacility = ApiFacility & { distance_km: number } type ApiOfficialCourseOption = { teeoff_course_id: number name: string is_main_course: boolean tees: ApiTeeOption[] holes: ApiCourseHole[] full_tees: ApiCourseTee[] } type ApiPersonalCourse = { id: string; name: string; is_mine: boolean; created_by_display_name: string } type ApiPersonalCourseDetail = { id: string name: string tees: ApiTeeOption[] holes: ApiCourseHole[] full_tees: ApiCourseTee[] is_mine: boolean created_by_display_name: string forked_from_id: string | null } export type PersonMatch = { id: string first_name: string last_name: string avatar_url: string | null home_club: string | null handicap_index: number | null } type ApiKnownGuest = { guest_first_name: string guest_last_name: string | null gender: ApiGender | null handicap_index: number | null } let idSeq = 0 function newId(prefix: string) { idSeq += 1 return `${prefix}-${idSeq}` } function apiTeesToTees(apiTees: ApiTeeOption[]): Tee[] { return apiTees.map((t, i) => ({ id: `${t.name}-${i}`, name: t.name, genders: t.genders })) } function courseFromOfficial(teeoffCourseId: number, c: { name: string tees: ApiTeeOption[] holes: ApiCourseHole[] full_tees: ApiCourseTee[] }): Course { return { id: `teeoff-${teeoffCourseId}`, name: c.name, tees: apiTeesToTees(c.tees), templateHoles: c.holes, templateTees: c.full_tees, } } // --- Offisielt banesøk ------------------------------------------------------- export async function searchOfficialFacilities(query: string): Promise { const res = await fetch(`/rounds/official-search?q=${encodeURIComponent(query)}`, { credentials: "include" }) if (!res.ok) return [] const data: ApiFacility[] = await res.json() return data } export async function nearbyOfficialFacilities(lat: number, lng: number): Promise<(Facility & { distanceKm: number })[]> { const res = await fetch(`/rounds/official-search/nearby?lat=${lat}&lng=${lng}&limit=5`, { credentials: "include" }) if (!res.ok) return [] const data: ApiNearbyFacility[] = await res.json() return data.map((f) => ({ slug: f.slug, name: f.name, city: f.city, county: f.county, distanceKm: f.distance_km })) } export async function fetchFacilityCourses(slug: string): Promise<{ teeoffCourseId: number; course: Course }[]> { const res = await fetch(`/rounds/official-search/${slug}`, { credentials: "include" }) if (!res.ok) return [] const data: { slug: string; name: string; courses: ApiOfficialCourseOption[] } = await res.json() return data.courses.map((c) => ({ teeoffCourseId: c.teeoff_course_id, course: courseFromOfficial(c.teeoff_course_id, c), })) } // --- Egne baner --------------------------------------------------------------- export async function searchOwnCourses(query: string): Promise<{ id: string; name: string }[]> { const res = await fetch(`/personal-courses?q=${encodeURIComponent(query)}`, { credentials: "include" }) if (!res.ok) return [] const data: ApiPersonalCourse[] = await res.json() return data.map((c) => ({ id: c.id, name: c.name })) } export async function fetchOwnCourse(id: string): Promise { const res = await fetch(`/personal-courses/${id}`, { credentials: "include" }) if (!res.ok) return null const c: ApiPersonalCourseDetail = await res.json() return { id: c.id, name: c.name, tees: apiTeesToTees(c.tees), templateHoles: c.holes, templateTees: c.full_tees, } } export async function createOwnCourse(body: { name: string holes: { hole_number: number; par: number; stroke_index: number }[] tees: { name: string; ratings: { gender: ApiGender; course_rating: number; slope_rating: number; par: number }[] }[] forkedFromId?: string | null }): Promise { const res = await fetch("/personal-courses", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ name: body.name, holes: body.holes, tees: body.tees, forked_from_id: body.forkedFromId ?? null, }), }) if (!res.ok) throw new Error(`create: ${res.status}`) const created: { id: string; name: string } = await res.json() return { id: created.id, name: created.name, tees: body.tees.map((t) => ({ id: newId(t.name), name: t.name, genders: t.ratings.map((r) => r.gender), })), } } // --- Spillersøk / kjent gjest ------------------------------------------------- export async function searchAccounts(query: string): Promise { const res = await fetch(`/people/search?q=${encodeURIComponent(query)}`, { credentials: "include" }) if (!res.ok) return [] const data: PersonMatch[] = await res.json() return data.map((p) => ({ id: p.id, firstName: p.first_name, lastName: p.last_name, handicap: p.handicap_index })) } export async function lookupKnownGuest(email: string): Promise { const res = await fetch(`/rounds/guests/known?email=${encodeURIComponent(email)}`, { credentials: "include" }) if (!res.ok) return null const g: ApiKnownGuest | null = await res.json() if (!g) return null return { firstName: g.guest_first_name, lastName: g.guest_last_name ?? "", gender: g.gender, hcp: g.handicap_index, } }