Add an artist listing page and improve SEO for various pages
Introduces a new `/kuenstler` page, API endpoint `/api/artists`, and enhances meta tags on `index.html` and for crawler requests on `/kuenstler`. Adds `KuenstlerPage.tsx` and updates `routes.ts` and `index.html`. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 401e2ec0-e00d-4f10-9d0e-60f3d479f9a5 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: 69ee4fdb-c617-4cdd-b9f6-d3fab268d533 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/60d372ff-2c10-46c7-b01b-10c3435136b0/401e2ec0-e00d-4f10-9d0e-60f3d479f9a5/qFrskyV
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Link, useLocation } from 'wouter';
|
||||
import { ArrowLeft, Music, Users, Search, Menu, X } from 'lucide-react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import HeaderAd from '@/components/HeaderAd';
|
||||
|
||||
interface Artist {
|
||||
name: string;
|
||||
videoCount: number;
|
||||
}
|
||||
|
||||
export default function KuenstlerPage() {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
const [, setLocation] = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = 'Alle Künstler & Interpreten | Folx TV - Video';
|
||||
const metaDescription = document.querySelector('meta[name="description"]');
|
||||
if (metaDescription) {
|
||||
metaDescription.setAttribute('content', 'Entdecken Sie alle Volksmusik- und Schlager-Künstler auf Folx TV. Nummer 1 in Europa für Volksmusik und Schlager.');
|
||||
}
|
||||
}, []);
|
||||
|
||||
const { data, isLoading } = useQuery<{ artists: Artist[], total: number }>({
|
||||
queryKey: ['/api/artists'],
|
||||
});
|
||||
|
||||
const artists = data?.artists || [];
|
||||
const filteredArtists = searchQuery
|
||||
? artists.filter(a => a.name.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
: artists;
|
||||
|
||||
const letters = Array.from(new Set(filteredArtists.map(a => a.name.charAt(0).toUpperCase()))).sort((a, b) => a.localeCompare(b, 'de'));
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-500"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black text-white">
|
||||
<header className="bg-black/80 backdrop-blur-md border-b border-purple-500/30 sticky top-0 z-50">
|
||||
<div className="max-w-7xl mx-auto px-4 py-3 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/">
|
||||
<span className="text-2xl font-bold bg-gradient-to-r from-purple-400 to-blue-400 bg-clip-text text-transparent cursor-pointer">
|
||||
Folx TV
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<nav className="hidden md:flex items-center gap-6">
|
||||
<Link href="/" className="text-gray-300 hover:text-white transition-colors">Startseite</Link>
|
||||
<Link href="/folx-stadl" className="text-gray-300 hover:text-white transition-colors">FOLX STADL</Link>
|
||||
<Link href="/geschichte-lied" className="text-gray-300 hover:text-white transition-colors">Geschichte des Liedes</Link>
|
||||
<Link href="/gipfelstammtisch" className="text-gray-300 hover:text-white transition-colors">Gipfelstammtisch</Link>
|
||||
<Link href="/kuenstler" className="text-purple-400 font-semibold">Künstler</Link>
|
||||
<Link href="/live" className="text-red-400 hover:text-red-300 transition-colors flex items-center gap-1">
|
||||
<span className="w-2 h-2 bg-red-500 rounded-full animate-pulse"></span> LIVE
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<button className="md:hidden text-white" onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}>
|
||||
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isMobileMenuOpen && (
|
||||
<nav className="md:hidden bg-black/95 border-t border-purple-500/20 py-4 px-4 space-y-3">
|
||||
<Link href="/" className="block text-gray-300 hover:text-white py-2">Startseite</Link>
|
||||
<Link href="/folx-stadl" className="block text-gray-300 hover:text-white py-2">FOLX STADL</Link>
|
||||
<Link href="/geschichte-lied" className="block text-gray-300 hover:text-white py-2">Geschichte des Liedes</Link>
|
||||
<Link href="/gipfelstammtisch" className="block text-gray-300 hover:text-white py-2">Gipfelstammtisch</Link>
|
||||
<Link href="/kuenstler" className="block text-purple-400 font-semibold py-2">Künstler</Link>
|
||||
<Link href="/live" className="block text-red-400 py-2">LIVE</Link>
|
||||
</nav>
|
||||
)}
|
||||
</header>
|
||||
|
||||
<HeaderAd />
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 py-8">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<Link href="/">
|
||||
<ArrowLeft className="text-gray-400 hover:text-white cursor-pointer" size={24} />
|
||||
</Link>
|
||||
<Users className="text-purple-400" size={28} />
|
||||
<h1 className="text-3xl font-bold">Alle Künstler & Interpreten</h1>
|
||||
<span className="bg-purple-500/20 text-purple-300 px-3 py-1 rounded-full text-sm">
|
||||
{artists.length} Künstler
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative mb-8 max-w-md">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" size={18} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Künstler suchen..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full pl-10 pr-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-purple-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mb-8">
|
||||
{letters.map(letter => (
|
||||
<a
|
||||
key={letter}
|
||||
href={`#letter-${letter}`}
|
||||
className="w-9 h-9 flex items-center justify-center bg-gray-800 hover:bg-purple-600 rounded-lg text-sm font-bold transition-colors"
|
||||
>
|
||||
{letter}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{letters.map(letter => {
|
||||
const letterArtists = filteredArtists.filter(a => a.name.charAt(0).toUpperCase() === letter);
|
||||
return (
|
||||
<div key={letter} id={`letter-${letter}`} className="mb-8">
|
||||
<h2 className="text-2xl font-bold text-purple-400 mb-4 border-b border-purple-500/30 pb-2">{letter}</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
|
||||
{letterArtists.map(artist => (
|
||||
<Link
|
||||
key={artist.name}
|
||||
href={`/?search=${encodeURIComponent(artist.name)}`}
|
||||
className="flex items-center gap-3 p-3 bg-gray-800/40 hover:bg-gray-700/60 rounded-lg transition-colors group"
|
||||
>
|
||||
<div className="w-10 h-10 bg-purple-500/20 rounded-full flex items-center justify-center group-hover:bg-purple-500/40 transition-colors">
|
||||
<Music size={18} className="text-purple-400" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-white font-medium truncate">{artist.name}</p>
|
||||
<p className="text-gray-400 text-sm">{artist.videoCount} Video{artist.videoCount > 1 ? 's' : ''}</p>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{filteredArtists.length === 0 && (
|
||||
<div className="text-center py-16 text-gray-400">
|
||||
<Users size={48} className="mx-auto mb-4 opacity-50" />
|
||||
<p className="text-lg">Keine Künstler gefunden für "{searchQuery}"</p>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
|
||||
<footer className="bg-black/80 border-t border-gray-800 py-6 mt-12">
|
||||
<div className="max-w-7xl mx-auto px-4 text-center text-gray-500 text-sm">
|
||||
<p>Folx TV - Nummer 1 in Europa für Volksmusik und Schlager</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -56,6 +56,13 @@ export default function Home() {
|
||||
if (metaDescription) {
|
||||
metaDescription.setAttribute('content', 'Folx TV - Nummer 1 in Europa für Volksmusik und Schlager. Video Streaming Plattform mit Musik, Unterhaltung und Live-Streaming.');
|
||||
}
|
||||
let metaKeywords = document.querySelector('meta[name="keywords"]');
|
||||
if (!metaKeywords) {
|
||||
metaKeywords = document.createElement('meta');
|
||||
metaKeywords.setAttribute('name', 'keywords');
|
||||
document.head.appendChild(metaKeywords);
|
||||
}
|
||||
metaKeywords.setAttribute('content', 'Volksmusik, Schlager, Folx TV, Kastelruther Spatzen, Monika Martin, Oswald Sattler, Angela Wiedl, Die Grubertaler, Michael Hirte, Sašo Avsenik, Jonny Hill, Rosanna Rocci, Linda Feller, Julia Buchner, Melanie Payer, Marlena Martinelli, Musikvideo, Streaming');
|
||||
}, []);
|
||||
|
||||
// Update videos when new data comes in
|
||||
|
||||
Reference in New Issue
Block a user