Ferdig med endringer enkeltfilter
This commit is contained in:
parent
f624546ebf
commit
f17075da0f
4 changed files with 128 additions and 24 deletions
|
|
@ -3,6 +3,7 @@
|
||||||
import { STATUS_MAP } from "@/config/constants";
|
import { STATUS_MAP } from "@/config/constants";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect, useMemo, useState, type CSSProperties } from "react";
|
import { useEffect, useMemo, useState, type CSSProperties } from "react";
|
||||||
import { getPublicCourseDisplayName, type EnrichedFacility } from "@/app/facilityData";
|
import { getPublicCourseDisplayName, type EnrichedFacility } from "@/app/facilityData";
|
||||||
|
|
||||||
|
|
@ -503,6 +504,7 @@ export default function FacilitySearch({
|
||||||
onFilteredFacilitiesChange,
|
onFilteredFacilitiesChange,
|
||||||
filterHeading = "Søk golfbaner",
|
filterHeading = "Søk golfbaner",
|
||||||
}: FacilitySearchProps) {
|
}: FacilitySearchProps) {
|
||||||
|
const router = useRouter();
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [areaFilter, setAreaFilter] = useState(fixedAreaFilter);
|
const [areaFilter, setAreaFilter] = useState(fixedAreaFilter);
|
||||||
const [statusFilter, setStatusFilter] = useState("");
|
const [statusFilter, setStatusFilter] = useState("");
|
||||||
|
|
@ -511,7 +513,6 @@ export default function FacilitySearch({
|
||||||
const [weatherDayFilter, setWeatherDayFilter] = useState("");
|
const [weatherDayFilter, setWeatherDayFilter] = useState("");
|
||||||
const [weatherDayOptions, setWeatherDayOptions] = useState(() => getWeatherDayOptions());
|
const [weatherDayOptions, setWeatherDayOptions] = useState(() => getWeatherDayOptions());
|
||||||
const [architectFilter, setArchitectFilter] = useState("");
|
const [architectFilter, setArchitectFilter] = useState("");
|
||||||
const [facilityFilter, setFacilityFilter] = useState("");
|
|
||||||
const [sortMethod, setSortMethod] = useState<SortMethod>("updated");
|
const [sortMethod, setSortMethod] = useState<SortMethod>("updated");
|
||||||
const [userLocation, setUserLocation] = useState<{ lat: number; lng: number } | null>(null);
|
const [userLocation, setUserLocation] = useState<{ lat: number; lng: number } | null>(null);
|
||||||
const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false);
|
const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false);
|
||||||
|
|
@ -707,8 +708,6 @@ export default function FacilitySearch({
|
||||||
: weatherForecast.find((entry) => Number(entry?.day_offset) === selectedWeatherDayOffset);
|
: weatherForecast.find((entry) => Number(entry?.day_offset) === selectedWeatherDayOffset);
|
||||||
const matchesWeather = !weatherDayFilter || Boolean(weatherDay?.dry_daylight);
|
const matchesWeather = !weatherDayFilter || Boolean(weatherDay?.dry_daylight);
|
||||||
const matchesArchitect = !architectFilter || architectKey === architectFilter;
|
const matchesArchitect = !architectFilter || architectKey === architectFilter;
|
||||||
const matchesFacility = !facilityFilter || facility.slug === facilityFilter;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...facility,
|
...facility,
|
||||||
holeValue,
|
holeValue,
|
||||||
|
|
@ -730,7 +729,6 @@ export default function FacilitySearch({
|
||||||
matchesSpecial,
|
matchesSpecial,
|
||||||
matchesWeather,
|
matchesWeather,
|
||||||
matchesArchitect,
|
matchesArchitect,
|
||||||
matchesFacility,
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(
|
.filter(
|
||||||
|
|
@ -741,8 +739,7 @@ export default function FacilitySearch({
|
||||||
facility.matchesHoles &&
|
facility.matchesHoles &&
|
||||||
facility.matchesSpecial &&
|
facility.matchesSpecial &&
|
||||||
facility.matchesWeather &&
|
facility.matchesWeather &&
|
||||||
facility.matchesArchitect &&
|
facility.matchesArchitect
|
||||||
facility.matchesFacility
|
|
||||||
)
|
)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (sortMethod === "dist") {
|
if (sortMethod === "dist") {
|
||||||
|
|
@ -758,7 +755,6 @@ export default function FacilitySearch({
|
||||||
}, [
|
}, [
|
||||||
areaFilter,
|
areaFilter,
|
||||||
architectFilter,
|
architectFilter,
|
||||||
facilityFilter,
|
|
||||||
holeFilter,
|
holeFilter,
|
||||||
initialFacilities,
|
initialFacilities,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
|
|
@ -776,7 +772,6 @@ export default function FacilitySearch({
|
||||||
specialFilter,
|
specialFilter,
|
||||||
weatherDayFilter,
|
weatherDayFilter,
|
||||||
architectFilter,
|
architectFilter,
|
||||||
facilityFilter,
|
|
||||||
searchQuery.trim(),
|
searchQuery.trim(),
|
||||||
].filter(Boolean).length;
|
].filter(Boolean).length;
|
||||||
const summaryText = `${processedFacilities.length} baner • ${getAreaLabel(areaFilter, countyOptions)}${
|
const summaryText = `${processedFacilities.length} baner • ${getAreaLabel(areaFilter, countyOptions)}${
|
||||||
|
|
@ -786,6 +781,11 @@ export default function FacilitySearch({
|
||||||
const isCollapsibleHomeSearch = variant === "home";
|
const isCollapsibleHomeSearch = variant === "home";
|
||||||
const searchPanelOpen = !isCollapsibleHomeSearch || isMobileSearchOpen;
|
const searchPanelOpen = !isCollapsibleHomeSearch || isMobileSearchOpen;
|
||||||
|
|
||||||
|
const handleFacilitySelect = (slug: string) => {
|
||||||
|
if (!slug) return;
|
||||||
|
router.push(`/golfbaner/${slug}`);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isCollapsibleHomeSearch && filtersCount > 0) {
|
if (isCollapsibleHomeSearch && filtersCount > 0) {
|
||||||
setIsMobileSearchOpen(true);
|
setIsMobileSearchOpen(true);
|
||||||
|
|
@ -916,11 +916,11 @@ export default function FacilitySearch({
|
||||||
|
|
||||||
<FieldSelect
|
<FieldSelect
|
||||||
label="Golfanlegg"
|
label="Golfanlegg"
|
||||||
value={facilityFilter}
|
value=""
|
||||||
onChange={setFacilityFilter}
|
onChange={handleFacilitySelect}
|
||||||
labelClassName={labelClassName}
|
labelClassName={labelClassName}
|
||||||
>
|
>
|
||||||
<option value="">Alle golfanlegg</option>
|
<option value="">Gå direkte til golfanlegg</option>
|
||||||
{facilityOptions.map((option) => (
|
{facilityOptions.map((option) => (
|
||||||
<option key={option.value} value={option.value}>
|
<option key={option.value} value={option.value}>
|
||||||
{option.label}
|
{option.label}
|
||||||
|
|
@ -957,7 +957,6 @@ export default function FacilitySearch({
|
||||||
setSpecialFilter("");
|
setSpecialFilter("");
|
||||||
setWeatherDayFilter("");
|
setWeatherDayFilter("");
|
||||||
setArchitectFilter("");
|
setArchitectFilter("");
|
||||||
setFacilityFilter("");
|
|
||||||
setSortMethod(userLocation ? "dist" : "updated");
|
setSortMethod(userLocation ? "dist" : "updated");
|
||||||
}}
|
}}
|
||||||
className={`btn btn-md mt-[1.72rem] h-[52px] ${
|
className={`btn btn-md mt-[1.72rem] h-[52px] ${
|
||||||
|
|
|
||||||
|
|
@ -312,6 +312,8 @@ export default function AdminDashboard() {
|
||||||
const [facilities, setFacilities] = useState<any[]>([]);
|
const [facilities, setFacilities] = useState<any[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [selectedFacilities, setSelectedFacilities] = useState<number[]>([]);
|
const [selectedFacilities, setSelectedFacilities] = useState<number[]>([]);
|
||||||
|
const [facilityJumpSlug, setFacilityJumpSlug] = useState('');
|
||||||
|
const [showBackToTop, setShowBackToTop] = useState(false);
|
||||||
const [scrapeJobs, setScrapeJobs] = useState<ScrapeJob[]>([]);
|
const [scrapeJobs, setScrapeJobs] = useState<ScrapeJob[]>([]);
|
||||||
const [isQueueing, setIsQueueing] = useState(false);
|
const [isQueueing, setIsQueueing] = useState(false);
|
||||||
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
|
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
|
||||||
|
|
@ -334,6 +336,7 @@ export default function AdminDashboard() {
|
||||||
const [manualEditForm, setManualEditForm] = useState<ManualOverrideForm>(EMPTY_MANUAL_OVERRIDE_FORM);
|
const [manualEditForm, setManualEditForm] = useState<ManualOverrideForm>(EMPTY_MANUAL_OVERRIDE_FORM);
|
||||||
const [isManualSaving, setIsManualSaving] = useState(false);
|
const [isManualSaving, setIsManualSaving] = useState(false);
|
||||||
const [dismissedLatestJobKeys, setDismissedLatestJobKeys] = useState<Partial<Record<AdminTab, string>>>({});
|
const [dismissedLatestJobKeys, setDismissedLatestJobKeys] = useState<Partial<Record<AdminTab, string>>>({});
|
||||||
|
const mainContentRef = useRef<HTMLElement | null>(null);
|
||||||
|
|
||||||
const fetchFacilities = () => {
|
const fetchFacilities = () => {
|
||||||
adminFetch(`${API_URL}/admin/facilities`)
|
adminFetch(`${API_URL}/admin/facilities`)
|
||||||
|
|
@ -434,6 +437,19 @@ export default function AdminDashboard() {
|
||||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||||
}, [showMobileAdminMenu]);
|
}, [showMobileAdminMenu]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const container = mainContentRef.current;
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
setShowBackToTop(container.scrollTop > 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleScroll();
|
||||||
|
container.addEventListener('scroll', handleScroll, { passive: true });
|
||||||
|
return () => container.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const filteredFacilities = useMemo(() => {
|
const filteredFacilities = useMemo(() => {
|
||||||
if (statusFilter === 'alle') return facilities;
|
if (statusFilter === 'alle') return facilities;
|
||||||
return facilities.map(facility => {
|
return facilities.map(facility => {
|
||||||
|
|
@ -451,6 +467,36 @@ export default function AdminDashboard() {
|
||||||
}).filter(facility => facility.course_statuses && facility.course_statuses.length > 0);
|
}).filter(facility => facility.course_statuses && facility.course_statuses.length > 0);
|
||||||
}, [facilities, statusFilter]);
|
}, [facilities, statusFilter]);
|
||||||
|
|
||||||
|
const facilityJumpOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
[...facilities]
|
||||||
|
.filter((facility) => facility?.slug && facility?.name)
|
||||||
|
.sort((a, b) => String(a.name).localeCompare(String(b.name), 'nb-NO'))
|
||||||
|
.map((facility) => ({
|
||||||
|
slug: String(facility.slug),
|
||||||
|
label: String(facility.name),
|
||||||
|
city: String(facility.city || '').trim(),
|
||||||
|
})),
|
||||||
|
[facilities]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!facilityJumpSlug) return;
|
||||||
|
if (activeTab === 'banestatus' && statusFilter !== 'alle') {
|
||||||
|
setStatusFilter('alle');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetId = `facility-card-${facilityJumpSlug}`;
|
||||||
|
const frameId = window.requestAnimationFrame(() => {
|
||||||
|
const target = document.getElementById(targetId);
|
||||||
|
if (!target) return;
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => window.cancelAnimationFrame(frameId);
|
||||||
|
}, [activeTab, facilityJumpSlug, filteredFacilities, statusFilter]);
|
||||||
|
|
||||||
const latestJobSummary = useMemo(() => {
|
const latestJobSummary = useMemo(() => {
|
||||||
if (!latestJob?.result_summary) return '';
|
if (!latestJob?.result_summary) return '';
|
||||||
|
|
||||||
|
|
@ -1397,7 +1443,7 @@ export default function AdminDashboard() {
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{/* HOVEDINNHOLD */}
|
{/* HOVEDINNHOLD */}
|
||||||
<main className="flex-1 min-w-0 p-4 md:p-8 lg:p-10 h-screen overflow-auto">
|
<main ref={mainContentRef} className="flex-1 min-w-0 p-4 md:p-8 lg:p-10 h-screen overflow-auto">
|
||||||
<div className="bg-white rounded-[2rem] shadow-2xl p-6 lg:p-10 border border-white">
|
<div className="bg-white rounded-[2rem] shadow-2xl p-6 lg:p-10 border border-white">
|
||||||
<div className="mb-6 flex md:hidden">
|
<div className="mb-6 flex md:hidden">
|
||||||
<button
|
<button
|
||||||
|
|
@ -1746,15 +1792,35 @@ export default function AdminDashboard() {
|
||||||
Hvert anlegg vises som et eget arbeidskort, slik at du ser innhold, status og handlinger samlet uten sideveis scrolling.
|
Hvert anlegg vises som et eget arbeidskort, slik at du ser innhold, status og handlinger samlet uten sideveis scrolling.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<label className="inline-flex items-center gap-3 rounded-2xl bg-white px-4 py-3 text-xs font-black uppercase tracking-widest text-gray-500 shadow-sm">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-end">
|
||||||
<input
|
<label className="block min-w-[18rem]">
|
||||||
type="checkbox"
|
<span className="mb-2 block text-[10px] font-black uppercase tracking-[0.2em] text-gray-500">
|
||||||
className="h-5 w-5 cursor-pointer accent-[#8bc34a]"
|
Hopp til golfanlegg
|
||||||
checked={selectedFacilities.length === filteredFacilities.length && filteredFacilities.length > 0}
|
</span>
|
||||||
onChange={handleSelectAll}
|
<select
|
||||||
/>
|
value={facilityJumpSlug}
|
||||||
Velg alle i visningen
|
onChange={(e) => setFacilityJumpSlug(e.target.value)}
|
||||||
</label>
|
className="w-full rounded-2xl border-2 border-gray-200 bg-white px-4 py-3 text-sm font-bold text-[#11280f] outline-none transition-colors focus:border-[#8bc34a]"
|
||||||
|
>
|
||||||
|
<option value="">Velg anlegg</option>
|
||||||
|
{facilityJumpOptions.map((facility) => (
|
||||||
|
<option key={facility.slug} value={facility.slug}>
|
||||||
|
{facility.city ? `${facility.label} • ${facility.city}` : facility.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="inline-flex items-center gap-3 rounded-2xl bg-white px-4 py-3 text-xs font-black uppercase tracking-widest text-gray-500 shadow-sm">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="h-5 w-5 cursor-pointer accent-[#8bc34a]"
|
||||||
|
checked={selectedFacilities.length === filteredFacilities.length && filteredFacilities.length > 0}
|
||||||
|
onChange={handleSelectAll}
|
||||||
|
/>
|
||||||
|
Velg alle i visningen
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-6 rounded-[1.75rem] border border-[#dbe7cf] bg-white p-4 shadow-sm">
|
<div className="mb-6 rounded-[1.75rem] border border-[#dbe7cf] bg-white p-4 shadow-sm">
|
||||||
|
|
@ -1810,7 +1876,14 @@ export default function AdminDashboard() {
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
key={f.id}
|
key={f.id}
|
||||||
className={`rounded-[1.9rem] border p-5 shadow-sm transition-all md:p-6 ${accentStyle} ${selectedFacilities.includes(f.id) ? 'ring-2 ring-[#8bc34a]/35 shadow-lg' : ''}`}
|
id={`facility-card-${f.slug}`}
|
||||||
|
className={`rounded-[1.9rem] border p-5 shadow-sm transition-all scroll-mt-6 md:p-6 ${accentStyle} ${
|
||||||
|
selectedFacilities.includes(f.id)
|
||||||
|
? 'ring-2 ring-[#8bc34a]/35 shadow-lg'
|
||||||
|
: facilityJumpSlug === f.slug
|
||||||
|
? 'ring-2 ring-[#11280f]/15 shadow-lg'
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5">
|
||||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
||||||
|
|
@ -2323,6 +2396,19 @@ export default function AdminDashboard() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
{showBackToTop && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => mainContentRef.current?.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||||
|
className="btn btn-ink fixed bottom-4 right-4 z-[95] flex h-12 items-center justify-center gap-2 rounded-full px-4 shadow-2xl md:bottom-8 md:right-8 md:h-14"
|
||||||
|
aria-label="Tilbake til toppen"
|
||||||
|
title="Tilbake til toppen"
|
||||||
|
>
|
||||||
|
<span className="text-base leading-none">↑</span>
|
||||||
|
<span className="hidden text-[10px] font-black uppercase tracking-[0.18em] sm:inline">Toppen</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,12 +179,14 @@ const SOCIAL_ICONS: Record<string, React.ReactNode> = {
|
||||||
export default function FacilityDetailView({
|
export default function FacilityDetailView({
|
||||||
facility,
|
facility,
|
||||||
relatedArticles = { banebesok: [], meninger: [] },
|
relatedArticles = { banebesok: [], meninger: [] },
|
||||||
|
canOpenAdminShortcut = false,
|
||||||
}: {
|
}: {
|
||||||
facility: any;
|
facility: any;
|
||||||
relatedArticles?: {
|
relatedArticles?: {
|
||||||
banebesok: any[];
|
banebesok: any[];
|
||||||
meninger: any[];
|
meninger: any[];
|
||||||
};
|
};
|
||||||
|
canOpenAdminShortcut?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [showBackToTop, setShowBackToTop] = useState(false);
|
const [showBackToTop, setShowBackToTop] = useState(false);
|
||||||
const [currentSlide, setCurrentSlide] = useState(0);
|
const [currentSlide, setCurrentSlide] = useState(0);
|
||||||
|
|
@ -340,6 +342,16 @@ export default function FacilityDetailView({
|
||||||
)}
|
)}
|
||||||
<h1 className="text-5xl md:text-8xl font-black text-white mb-3 tracking-tighter drop-shadow-2xl">{facility.name}</h1>
|
<h1 className="text-5xl md:text-8xl font-black text-white mb-3 tracking-tighter drop-shadow-2xl">{facility.name}</h1>
|
||||||
<p className="text-[#7ca982] uppercase tracking-[0.4em] font-black text-xs md:text-sm pl-1">{facility.county} • {facility.city}</p>
|
<p className="text-[#7ca982] uppercase tracking-[0.4em] font-black text-xs md:text-sm pl-1">{facility.county} • {facility.city}</p>
|
||||||
|
{canOpenAdminShortcut && facilitySlug && (
|
||||||
|
<div className="mt-5">
|
||||||
|
<Link
|
||||||
|
href={`/admin/${facilitySlug}`}
|
||||||
|
className="inline-flex items-center rounded-full border border-white/15 bg-black/30 px-4 py-2 text-[10px] font-black uppercase tracking-[0.18em] text-white shadow-xl backdrop-blur-md transition hover:border-[#8bc34a]/70 hover:bg-black/45 hover:text-[#D9F0B9] md:text-[11px]"
|
||||||
|
>
|
||||||
|
Admin
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
import { cookies } from "next/headers";
|
||||||
import { notFound, permanentRedirect } from "next/navigation";
|
import { notFound, permanentRedirect } from "next/navigation";
|
||||||
import { API_URL } from "@/config/constants";
|
import { API_URL } from "@/config/constants";
|
||||||
import { resolveFacilityAlias } from "@/app/facilityAliases";
|
import { resolveFacilityAlias } from "@/app/facilityAliases";
|
||||||
|
|
@ -85,6 +86,7 @@ export async function generateMetadata({ params }: GolfCoursePageProps): Promise
|
||||||
|
|
||||||
export default async function GolfCoursePage({ params }: GolfCoursePageProps) {
|
export default async function GolfCoursePage({ params }: GolfCoursePageProps) {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
|
const cookieStore = await cookies();
|
||||||
const facility = await getFacility(slug);
|
const facility = await getFacility(slug);
|
||||||
|
|
||||||
if (!facility) {
|
if (!facility) {
|
||||||
|
|
@ -99,6 +101,7 @@ export default async function GolfCoursePage({ params }: GolfCoursePageProps) {
|
||||||
const facilityJsonLd = createFacilityJsonLd(facility);
|
const facilityJsonLd = createFacilityJsonLd(facility);
|
||||||
const vtgCourseJsonLd = createVtgCourseJsonLd(facility);
|
const vtgCourseJsonLd = createVtgCourseJsonLd(facility);
|
||||||
const relatedArticles = await getFacilityEditorialArticles(facility.slug, 3);
|
const relatedArticles = await getFacilityEditorialArticles(facility.slug, 3);
|
||||||
|
const canOpenAdminShortcut = Boolean(cookieStore.get("admin_session")?.value);
|
||||||
const breadcrumbJsonLd = createBreadcrumbJsonLd([
|
const breadcrumbJsonLd = createBreadcrumbJsonLd([
|
||||||
{ name: "Hjem", path: "/" },
|
{ name: "Hjem", path: "/" },
|
||||||
{ name: "Golfbaner", path: "/golfbaner" },
|
{ name: "Golfbaner", path: "/golfbaner" },
|
||||||
|
|
@ -121,7 +124,11 @@ export default async function GolfCoursePage({ params }: GolfCoursePageProps) {
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
|
||||||
/>
|
/>
|
||||||
<FacilityDetailView facility={facility} relatedArticles={relatedArticles} />
|
<FacilityDetailView
|
||||||
|
facility={facility}
|
||||||
|
relatedArticles={relatedArticles}
|
||||||
|
canOpenAdminShortcut={canOpenAdminShortcut}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue