Siste finjustering frontend

This commit is contained in:
Erol 2026-04-12 14:55:09 +02:00
parent 099eac4500
commit 0e6752e010
2 changed files with 85 additions and 68 deletions

View file

@ -17,6 +17,7 @@ type Facility = {
id: number;
slug: string;
name: string;
description?: string | null;
city?: string | null;
county?: string | null;
banetype?: string | null;
@ -212,6 +213,13 @@ const buildMapUrl = (lat?: number | null, lng?: number | null) => {
return `https://www.google.com/maps/search/?api=1&query=${lat},${lng}`;
};
const toPlainText = (value: string | null | undefined) =>
String(value || "")
.replace(/<[^>]+>/g, " ")
.replace(/&nbsp;/gi, " ")
.replace(/\s+/g, " ")
.trim();
const getAreaLabel = (value: string, countyOptions: Array<{ slug: string; label: string }>) => {
if (!value) return "Hele Norge";
const builtIn = HIERARCHICAL_AREA_OPTIONS.find((option) => option.value === value);
@ -256,7 +264,7 @@ const noteClampStyle: CSSProperties = {
};
const actionIconClassName =
"flex h-10 w-10 items-center justify-center rounded-[0.95rem] border border-[#D5DDD1] bg-white text-[#112015] transition hover:border-[#FF5722] hover:text-[#FF5722]";
"flex h-7 w-7 items-center justify-center rounded-[0.8rem] border border-[#D5DDD1] bg-white text-[#112015] transition hover:border-[#FF5722] hover:text-[#FF5722]";
export default function FacilitySearch({
initialFacilities,
@ -580,26 +588,36 @@ export default function FacilitySearch({
/>
<div className="absolute inset-0 bg-gradient-to-t from-[#25312A]/65 via-[#25312A]/10 to-transparent" />
<div className="absolute left-4 top-4 flex max-w-[calc(100%-2rem)] flex-wrap gap-2">
<span
className={`rounded-full px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] ${
STATUS_CLASSES[facility.primaryStatus] || STATUS_CLASSES.ukjent
}`}
>
{getStatusLabel(facility.primaryStatus)}
</span>
{facility.hasGolfamore && (
<span className="rounded-full bg-[#FF5722] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-white">
Golfamore
<div className="absolute left-4 top-4 flex max-w-[calc(100%-7rem)] flex-col items-start gap-2">
<div className="flex flex-wrap gap-2">
<span
className={`rounded-full px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] ${
STATUS_CLASSES[facility.primaryStatus] || STATUS_CLASSES.ukjent
}`}
>
{getStatusLabel(facility.primaryStatus)}
</span>
)}
{facility.hasNSG && (
<span className="rounded-full bg-[#2D6CB5] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-white">
NSG
</span>
)}
{facility.hasGolfamore && (
<span className="rounded-full bg-[#FF5722] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-white">
Golfamore
</span>
)}
{facility.hasNSG && (
<span className="rounded-full bg-[#2D6CB5] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-white">
NSG
</span>
)}
</div>
</div>
{facility.status_updated_at && (
<div className="absolute right-4 top-4">
<span className="inline-flex rounded-full border border-white/15 bg-[#25312A]/72 px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.18em] text-white/90 backdrop-blur-sm shadow-sm">
{formatUpdatedDate(facility.status_updated_at)}
</span>
</div>
)}
<div className="absolute bottom-4 left-4 right-4">
<p className="text-[10px] font-extrabold uppercase tracking-[0.22em] text-white/75">
{facility.city} {facility.county}
@ -611,26 +629,20 @@ export default function FacilitySearch({
</Link>
<div className="space-y-5 p-5">
<div className="flex flex-wrap gap-2">
<span className="rounded-full bg-[#EEF5E4] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-[#112015]">
{facility.holeValue || "--"} hull
</span>
<span className="rounded-full bg-[#F4F5F1] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-[#617063]">
{facility.banetype || "Banetype ukjent"}
</span>
</div>
<div className="flex flex-wrap gap-2">
<span className="rounded-full bg-[#F7F8F3] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-[#617063]">
Oppdatert {formatUpdatedDate(facility.status_updated_at)}
</span>
<span className="rounded-full bg-[#F7F8F3] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-[#617063]">
{sortMethod === "dist" && Number.isFinite(facility.distance)
? `${Math.round(facility.distance)} km unna`
: sortMethod === "updated"
? "Nyeste status"
: "Alfabetisk"}
</span>
<div className="flex items-start gap-3">
<div className="flex min-w-0 flex-wrap gap-2">
<span className="rounded-full bg-[#EEF5E4] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-[#112015]">
{facility.holeValue || "--"} hull
</span>
<span className="rounded-full bg-[#F4F5F1] px-3 py-1.5 text-[10px] font-extrabold uppercase tracking-[0.15em] text-[#617063]">
{facility.banetype || "Banetype ukjent"}
</span>
</div>
{Number.isFinite(facility.distance) && (
<span className="ml-auto shrink-0 self-center rounded-full bg-[#F7F8F3] px-3 py-1.5 text-right text-[10px] font-extrabold uppercase tracking-[0.15em] text-[#617063]">
{Math.round(facility.distance)} km unna
</span>
)}
</div>
{facility.footnote && (
@ -644,7 +656,15 @@ export default function FacilitySearch({
</div>
)}
<div className="flex flex-wrap justify-center gap-2">
{toPlainText(facility.description) && (
<p className="text-[15px] leading-7 text-[#617063]">
{toPlainText(facility.description)}
</p>
)}
<div className="grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 text-sm font-bold text-[#112015]">
<span className="truncate text-[#617063]">{facility.phone ? facility.phone : facility.city || "Se detaljer"}</span>
<div className="flex items-center justify-self-center gap-1">
{facility.website_url && (
<a href={facility.website_url} target="_blank" rel="noreferrer" className={actionIconClassName} aria-label={`Besøk nettsiden til ${facility.name}`}>
<ActionIcon type="web" />
@ -670,12 +690,9 @@ export default function FacilitySearch({
<ActionIcon type="weather" />
</a>
)}
</div>
<div className="flex items-center justify-between gap-3 text-sm font-bold text-[#112015]">
<span className="truncate text-[#617063]">{facility.phone ? facility.phone : facility.city || "Se detaljer"}</span>
<Link href={`/golfbaner/${facility.slug}`} className="shrink-0 text-[#FF5722] transition hover:text-[#C94F2D]">
Se anlegg
</div>
<Link href={`/golfbaner/${facility.slug}`} className="justify-self-end shrink-0 text-right text-[#FF5722] transition hover:text-[#C94F2D]">
Se anlegg
</Link>
</div>
</div>
@ -690,7 +707,7 @@ export default function FacilitySearch({
function ActionIcon({ type }: { type: "web" | "booking" | "trophy" | "pin" | "weather" }) {
return (
<svg
className="h-5 w-5"
className="h-3.5 w-3.5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"

View file

@ -129,36 +129,36 @@ export default function CourseDisplay({ course }: { course: any }) {
);
return (
<div className="bg-white rounded-[3rem] shadow-sm border border-gray-200 overflow-hidden mb-12">
<div className="mb-12 w-full min-w-0 max-w-[100vw] overflow-x-clip rounded-[2rem] border border-gray-200 bg-white shadow-sm md:rounded-[3rem]">
{/* HEADER / KALKULATOR */}
<div className="p-8 md:p-12 flex flex-col md:flex-row justify-between items-center gap-8 border-b border-gray-100 bg-white">
<div className="text-center md:text-left">
<h2 className="text-5xl font-black text-[#11280f] tracking-tighter">{course.name}</h2>
<p className="text-[#7ca982] font-black uppercase text-xs tracking-[0.2em] mt-2 mb-1">
Par {course.par} {course.length_meters || '--'} meter
</p>
<p className="text-gray-400 text-[10px] font-bold uppercase tracking-widest">
Rating utløper: {slopeExpiry}
</p>
</div>
<div className="flex min-w-0 flex-col items-stretch justify-between gap-5 border-b border-gray-100 bg-white p-4 sm:p-5 md:flex-row md:items-center md:gap-8 md:p-12">
<div className="min-w-0 text-left">
<h2 className="break-words text-4xl font-black tracking-tighter text-[#11280f] sm:text-5xl">{course.name}</h2>
<p className="text-[#7ca982] font-black uppercase text-xs tracking-[0.2em] mt-2 mb-1">
Par {course.par} {course.length_meters || '--'} meter
</p>
<p className="text-gray-400 text-[10px] font-bold uppercase tracking-widest">
Rating utløper: {slopeExpiry}
</p>
</div>
<div className="grid w-full max-w-[28rem] grid-cols-2 gap-4 bg-gray-50 p-4 rounded-[2rem] border border-gray-100 md:flex md:w-auto md:max-w-none md:items-center md:gap-6 md:p-6">
<div className="flex flex-col"><span className="text-[9px] font-black text-[#7ca982] uppercase ml-1">Kjønn</span>
<select value={gender} onChange={e => { setGender(e.target.value as any); setSelectedTeeIndex(0); }} className="min-w-0 bg-transparent text-[#11280f] font-black outline-none border-b-2 border-[#7ca982]/30 pb-1 cursor-pointer">
<option value="herrer">HERRER</option><option value="damer">DAMER</option>
<div className="grid w-full min-w-0 max-w-full grid-cols-1 gap-3 rounded-[1.6rem] border border-gray-100 bg-gray-50 p-4 sm:grid-cols-2 md:flex md:w-auto md:max-w-none md:items-center md:gap-6 md:rounded-[2rem] md:p-6">
<div className="flex min-w-0 flex-col"><span className="ml-1 text-[9px] font-black uppercase text-[#7ca982]">Kjønn</span>
<select value={gender} onChange={e => { setGender(e.target.value as any); setSelectedTeeIndex(0); }} className="w-full min-w-0 truncate border-b-2 border-[#7ca982]/30 bg-transparent pb-1 pr-6 text-[#11280f] font-black outline-none cursor-pointer">
<option value="herrer">HERRER</option><option value="damer">DAMER</option>
</select>
</div>
<div className="flex flex-col"><span className="text-[9px] font-black text-[#7ca982] uppercase ml-1">Utslag</span>
<select value={selectedTeeIndex} onChange={e => setSelectedTeeIndex(Number(e.target.value))} className="min-w-0 bg-transparent text-[#11280f] font-black outline-none border-b-2 border-[#7ca982]/30 pb-1 cursor-pointer">
{availableTees.map((t: any, i: number) => (<option key={i} value={i}>{t.navn_utslag || t.navn_utslag_damer}</option>))}
<div className="flex min-w-0 flex-col"><span className="ml-1 text-[9px] font-black uppercase text-[#7ca982]">Utslag</span>
<select value={selectedTeeIndex} onChange={e => setSelectedTeeIndex(Number(e.target.value))} className="w-full min-w-0 truncate border-b-2 border-[#7ca982]/30 bg-transparent pb-1 pr-6 text-[#11280f] font-black outline-none cursor-pointer">
{availableTees.map((t: any, i: number) => (<option key={i} value={i}>{t.navn_utslag || t.navn_utslag_damer}</option>))}
</select>
</div>
<div className="flex flex-col"><span className="text-[9px] font-black text-[#7ca982] uppercase ml-1">Ditt HCP</span>
<input type="text" value={hcp} onChange={e => setHcp(e.target.value)} className="w-12 bg-transparent text-[#11280f] font-black text-center border-b-2 border-[#7ca982]/30" />
<div className="flex min-w-0 flex-col"><span className="ml-1 text-[9px] font-black uppercase text-[#7ca982]">Ditt HCP</span>
<input type="text" value={hcp} onChange={e => setHcp(e.target.value)} className="w-full min-w-0 border-b-2 border-[#7ca982]/30 bg-transparent px-1 text-left font-black text-[#11280f] outline-none md:w-16 md:text-center" />
</div>
<div className="col-span-2 flex items-center justify-center gap-3 rounded-[1.4rem] bg-white px-4 py-3 text-center md:col-span-1 md:pl-6 md:border-l md:border-gray-200 md:bg-transparent md:px-0 md:py-0">
<p className="text-[9px] uppercase font-black text-[#7ca982] mb-1">SpH</p>
<div className="flex items-center justify-between gap-3 rounded-[1.4rem] bg-white px-4 py-3 md:col-span-1 md:justify-center md:border-l md:border-gray-200 md:bg-transparent md:px-0 md:py-0 md:pl-6">
<p className="mb-1 text-[9px] font-black uppercase text-[#7ca982]">SpH</p>
<p className="text-4xl font-black text-[#11280f] leading-none">{playingHandicap || 0}</p>
</div>
</div>