feat(web): remove legacy [state] routes — redirect now in hooks.server.ts
This commit is contained in:
@@ -1,48 +0,0 @@
|
||||
import type { PageServerLoad } from './$types.js';
|
||||
import { apiFetch } from '$lib/api/client.js';
|
||||
import type { MarketSummary } from '$lib/api/types.js';
|
||||
import { slugToState, stateToSlug, toSlug } from '$lib/utils/slug.js';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export interface CityInfo {
|
||||
slug: string;
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export const load: PageServerLoad = async ({ params, fetch }) => {
|
||||
const stateName = slugToState(params.state);
|
||||
if (!stateName) {
|
||||
error(404, { message: 'Bundesland nicht gefunden.' });
|
||||
}
|
||||
|
||||
let allMarkets: MarketSummary[] = [];
|
||||
try {
|
||||
const res = await apiFetch<MarketSummary[]>('/markets?per_page=1000', { fetch });
|
||||
allMarkets = res.data;
|
||||
} catch {
|
||||
// Backend unreachable
|
||||
}
|
||||
|
||||
const markets = allMarkets.filter((m) => stateToSlug(m.state) === params.state);
|
||||
|
||||
if (markets.length === 0) {
|
||||
error(404, { message: `Keine Märkte in ${stateName} gefunden.` });
|
||||
}
|
||||
|
||||
const countByCity = new Map<string, number>();
|
||||
for (const m of markets) {
|
||||
countByCity.set(m.city, (countByCity.get(m.city) ?? 0) + 1);
|
||||
}
|
||||
|
||||
const cities: CityInfo[] = Array.from(countByCity.entries())
|
||||
.map(([name, count]) => ({ slug: toSlug(name), name, count }))
|
||||
.sort((a, b) => a.name.localeCompare(b.name, 'de'));
|
||||
|
||||
return {
|
||||
stateName,
|
||||
stateSlug: params.state,
|
||||
markets,
|
||||
cities
|
||||
};
|
||||
};
|
||||
@@ -1,122 +0,0 @@
|
||||
<script lang="ts">
|
||||
import MarketCard from '$lib/components/market/MarketCard.svelte';
|
||||
import type { CityInfo } from './+page.server.js';
|
||||
import type { MarketSummary } from '$lib/api/types.js';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
let { data } = $props();
|
||||
const stateName: string = $derived(data.stateName);
|
||||
const stateSlug: string = $derived(data.stateSlug);
|
||||
const markets: MarketSummary[] = $derived(data.markets);
|
||||
const cities: CityInfo[] = $derived(data.cities);
|
||||
const origin = $derived($page.url.origin);
|
||||
|
||||
const jsonLdBreadcrumbHtml = $derived(
|
||||
'<script type="application/ld+json">' +
|
||||
JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 1,
|
||||
name: 'Startseite',
|
||||
item: `${origin}/`
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 2,
|
||||
name: 'Märkte nach Bundesland',
|
||||
item: `${origin}/maerkte/`
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 3,
|
||||
name: stateName,
|
||||
item: `${origin}/maerkte/${stateSlug}/`
|
||||
}
|
||||
]
|
||||
}) +
|
||||
'</' +
|
||||
'script>'
|
||||
);
|
||||
|
||||
const jsonLdItemListHtml = $derived(
|
||||
'<script type="application/ld+json">' +
|
||||
JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ItemList',
|
||||
name: `Mittelaltermärkte in ${stateName}`,
|
||||
numberOfItems: markets.length,
|
||||
itemListElement: markets.slice(0, 30).map((m, i) => ({
|
||||
'@type': 'ListItem',
|
||||
position: i + 1,
|
||||
url: `${origin}/markt/${m.slug}`
|
||||
}))
|
||||
}) +
|
||||
'</' +
|
||||
'script>'
|
||||
);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Mittelaltermärkte in {stateName} - Marktvogt</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Finde {markets.length} Mittelaltermärkte in {stateName}. Ritterturniere, historische Feste und mittelalterliche Spektakel nach Stadt durchsuchen."
|
||||
/>
|
||||
<meta property="og:title" content="Mittelaltermärkte in {stateName} - Marktvogt" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="{markets.length} Mittelaltermärkte in {stateName} entdecken."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
{@html jsonLdBreadcrumbHtml}
|
||||
{@html jsonLdItemListHtml}
|
||||
</svelte:head>
|
||||
|
||||
<div class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
|
||||
<nav class="mb-6 text-sm text-stone-500 dark:text-stone-400" aria-label="Breadcrumb">
|
||||
<ol class="flex items-center gap-1.5">
|
||||
<li><a href="/" class="hover:text-stone-700 dark:hover:text-stone-200">Startseite</a></li>
|
||||
<li aria-hidden="true">/</li>
|
||||
<li>
|
||||
<a href="/maerkte/" class="hover:text-stone-700 dark:hover:text-stone-200">Bundesländer</a>
|
||||
</li>
|
||||
<li aria-hidden="true">/</li>
|
||||
<li class="text-stone-900 dark:text-stone-100">{stateName}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<h1 class="text-3xl font-bold text-stone-900 sm:text-4xl dark:text-stone-100">
|
||||
Mittelaltermärkte in {stateName}
|
||||
</h1>
|
||||
<p class="mt-2 text-lg text-stone-600 dark:text-stone-300">
|
||||
{markets.length}
|
||||
{markets.length === 1 ? 'Markt' : 'Märkte'} in {stateName}
|
||||
</p>
|
||||
|
||||
{#if cities.length > 1}
|
||||
<div class="mt-6">
|
||||
<h2 class="text-sm font-medium tracking-wider text-stone-500 uppercase dark:text-stone-400">
|
||||
Städte
|
||||
</h2>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
{#each cities as city (city.slug)}
|
||||
<a
|
||||
href="/maerkte/{stateSlug}/{city.slug}/"
|
||||
class="bg-vellum hover:border-primary-300 hover:text-primary-700 dark:hover:border-primary-500 dark:hover:text-primary-400 rounded-full border border-stone-200 px-3 py-1 text-sm text-stone-700 transition-colors dark:border-stone-600 dark:text-stone-300"
|
||||
>
|
||||
{city.name} ({city.count})
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each markets as market (market.id)}
|
||||
<MarketCard {market} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,38 +0,0 @@
|
||||
import type { PageServerLoad } from './$types.js';
|
||||
import { apiFetch } from '$lib/api/client.js';
|
||||
import type { MarketSummary } from '$lib/api/types.js';
|
||||
import { slugToState, stateToSlug, toSlug } from '$lib/utils/slug.js';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, fetch }) => {
|
||||
const stateName = slugToState(params.state);
|
||||
if (!stateName) {
|
||||
error(404, { message: 'Bundesland nicht gefunden.' });
|
||||
}
|
||||
|
||||
let allMarkets: MarketSummary[] = [];
|
||||
try {
|
||||
const res = await apiFetch<MarketSummary[]>('/markets?per_page=1000', { fetch });
|
||||
allMarkets = res.data;
|
||||
} catch {
|
||||
// Backend unreachable
|
||||
}
|
||||
|
||||
const markets = allMarkets.filter(
|
||||
(m) => stateToSlug(m.state) === params.state && toSlug(m.city) === params.city
|
||||
);
|
||||
|
||||
if (markets.length === 0) {
|
||||
error(404, { message: 'Keine Märkte in dieser Stadt gefunden.' });
|
||||
}
|
||||
|
||||
const cityName = markets[0].city;
|
||||
|
||||
return {
|
||||
stateName,
|
||||
stateSlug: params.state,
|
||||
cityName,
|
||||
citySlug: params.city,
|
||||
markets
|
||||
};
|
||||
};
|
||||
@@ -1,115 +0,0 @@
|
||||
<script lang="ts">
|
||||
import MarketCard from '$lib/components/market/MarketCard.svelte';
|
||||
import type { MarketSummary } from '$lib/api/types.js';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
let { data } = $props();
|
||||
const stateName: string = $derived(data.stateName);
|
||||
const stateSlug: string = $derived(data.stateSlug);
|
||||
const cityName: string = $derived(data.cityName);
|
||||
const markets: MarketSummary[] = $derived(data.markets);
|
||||
const origin = $derived($page.url.origin);
|
||||
|
||||
const jsonLdBreadcrumbHtml = $derived(
|
||||
'<script type="application/ld+json">' +
|
||||
JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 1,
|
||||
name: 'Startseite',
|
||||
item: `${origin}/`
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 2,
|
||||
name: 'Märkte nach Bundesland',
|
||||
item: `${origin}/maerkte/`
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 3,
|
||||
name: stateName,
|
||||
item: `${origin}/maerkte/${stateSlug}/`
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 4,
|
||||
name: cityName,
|
||||
item: `${origin}/maerkte/${stateSlug}/${data.citySlug}/`
|
||||
}
|
||||
]
|
||||
}) +
|
||||
'</' +
|
||||
'script>'
|
||||
);
|
||||
|
||||
const jsonLdItemListHtml = $derived(
|
||||
'<script type="application/ld+json">' +
|
||||
JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ItemList',
|
||||
name: `Mittelaltermärkte in ${cityName}`,
|
||||
numberOfItems: markets.length,
|
||||
itemListElement: markets.map((m, i) => ({
|
||||
'@type': 'ListItem',
|
||||
position: i + 1,
|
||||
url: `${origin}/markt/${m.slug}`
|
||||
}))
|
||||
}) +
|
||||
'</' +
|
||||
'script>'
|
||||
);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Mittelaltermärkte in {cityName}, {stateName} - Marktvogt</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Finde {markets.length} Mittelaltermärkte in {cityName}, {stateName}. Ritterturniere, historische Feste und mittelalterliche Spektakel."
|
||||
/>
|
||||
<meta property="og:title" content="Mittelaltermärkte in {cityName}, {stateName} - Marktvogt" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="{markets.length} Mittelaltermärkte in {cityName} entdecken."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
{@html jsonLdBreadcrumbHtml}
|
||||
{@html jsonLdItemListHtml}
|
||||
</svelte:head>
|
||||
|
||||
<div class="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
|
||||
<nav class="mb-6 text-sm text-stone-500 dark:text-stone-400" aria-label="Breadcrumb">
|
||||
<ol class="flex items-center gap-1.5">
|
||||
<li><a href="/" class="hover:text-stone-700 dark:hover:text-stone-200">Startseite</a></li>
|
||||
<li aria-hidden="true">/</li>
|
||||
<li>
|
||||
<a href="/maerkte/" class="hover:text-stone-700 dark:hover:text-stone-200">Bundesländer</a>
|
||||
</li>
|
||||
<li aria-hidden="true">/</li>
|
||||
<li>
|
||||
<a href="/maerkte/{stateSlug}/" class="hover:text-stone-700 dark:hover:text-stone-200"
|
||||
>{stateName}</a
|
||||
>
|
||||
</li>
|
||||
<li aria-hidden="true">/</li>
|
||||
<li class="text-stone-900 dark:text-stone-100">{cityName}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<h1 class="text-3xl font-bold text-stone-900 sm:text-4xl dark:text-stone-100">
|
||||
Mittelaltermärkte in {cityName}
|
||||
</h1>
|
||||
<p class="mt-2 text-lg text-stone-600 dark:text-stone-300">
|
||||
{markets.length}
|
||||
{markets.length === 1 ? 'Markt' : 'Märkte'} in {cityName}, {stateName}
|
||||
</p>
|
||||
|
||||
<div class="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each markets as market (market.id)}
|
||||
<MarketCard {market} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user