Siste finjustering frontend
This commit is contained in:
parent
099eac4500
commit
0e6752e010
2 changed files with 85 additions and 68 deletions
|
|
@ -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(/ /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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue