videofolxtv/client/src/pages/home.tsx
sebastjanartic dd8ec301e2 Integrate ads to enhance platform monetization and user engagement
Introduce AdSenseAd component and integrate it across multiple pages (VideoPage, Home) to display various ad formats, including header, sidebar, and mid-content placements.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 946a0075-7e32-454b-b348-9e7f576d7f45
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/60d372ff-2c10-46c7-b01b-10c3435136b0/946a0075-7e32-454b-b348-9e7f576d7f45/tiAdL5T
2025-09-04 16:24:43 +00:00

277 lines
12 KiB
TypeScript

import { useState, useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import { type Video } from "@shared/schema";
import NetflixGrid from "@/components/netflix-grid";
import { Link } from "wouter";
import { Input } from "@/components/ui/input";
import { Search, Menu, X } from "lucide-react";
import AdSenseAd from "@/components/adsense-ad";
interface VideosResponse {
videos: Video[];
total: number;
hasMore: boolean;
}
export default function Home() {
const [searchQuery, setSearchQuery] = useState("");
const [allVideos, setAllVideos] = useState<Video[]>([]);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
// Fetch videos with optimized loading
const { data: videosResponse, isLoading, refetch } = useQuery<VideosResponse>({
queryKey: ["/api/videos", {
limit: 150,
offset: 0,
search: searchQuery || undefined
}],
queryFn: async ({ queryKey }) => {
const [, params] = queryKey as [string, any];
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
searchParams.append(key, String(value));
}
});
const response = await fetch(`/api/videos?${searchParams}`);
if (!response.ok) {
throw new Error('Failed to fetch videos');
}
return response.json();
},
staleTime: 5 * 60 * 1000,
gcTime: 10 * 60 * 1000,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
// Real-time refresh for view counts - refresh every 2 minutes for better performance
refetchInterval: 120000
});
// Update videos when new data comes in
useEffect(() => {
if (videosResponse) {
setAllVideos(videosResponse.videos);
}
}, [videosResponse]);
// Debounce search to reduce API calls (500ms delay)
useEffect(() => {
const debounceTimer = setTimeout(() => {
if (searchQuery !== undefined) {
refetch();
}
}, 500);
return () => clearTimeout(debounceTimer);
}, [searchQuery, refetch]);
return (
<div className="has-fixed-header" style={{minHeight: '100vh', background: 'linear-gradient(135deg, hsl(220, 13%, 9%) 0%, hsl(220, 13%, 12%) 50%, hsl(220, 13%, 15%) 100%)', color: 'white'}}>
{/* STICKY HEADER */}
<div className="header-sticky bg-transparent overflow-hidden">
<div className="container py-6">
<div className="flex items-center justify-between">
{/* Left side - Logo */}
<div className="flex items-center space-x-6 flex-1">
<Link href="/" className="flex items-center space-x-3 hover:opacity-80 transition-opacity py-4">
<div className="w-10 h-10 bg-[#da234d] rounded-lg flex items-center justify-center shadow-lg">
<div className="w-0 h-0 border-l-[11px] border-l-white border-y-[8px] border-y-transparent ml-1"></div>
</div>
<h1 className="text-2xl font-bold text-white tracking-wide">video.folx.tv</h1>
</Link>
</div>
{/* Right side - Navigation + Search */}
<div className="flex items-center gap-4">
{/* Desktop navigation */}
<div className="hidden md:flex items-center space-x-6">
<nav className="flex space-x-6">
<Link href="/" className="relative text-bunny-light hover:text-white transition-colors group">
Startseite
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-rose-400 via-pink-500 to-rose-600 transition-all duration-300 group-hover:w-full"></span>
</Link>
<Link href="/folx-stadl" className="relative text-bunny-light hover:text-white transition-colors group">
FOLX STADL
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-rose-400 via-pink-500 to-rose-600 transition-all duration-300 group-hover:w-full"></span>
</Link>
<Link href="/geschichte-lied" className="relative text-bunny-light hover:text-white transition-colors group">
DIE GESCHICHTE DES LIEDES
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-rose-400 via-pink-500 to-rose-600 transition-all duration-300 group-hover:w-full"></span>
</Link>
<Link href="/gipfelstammtisch" className="relative text-bunny-light hover:text-white transition-colors group">
GIPFELSTAMMTISCH
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-rose-400 via-pink-500 to-rose-600 transition-all duration-300 group-hover:w-full"></span>
</Link>
</nav>
<div className="relative">
<Input
type="search"
placeholder="Suchen..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="bg-white border border-gray-300 rounded-lg px-4 py-1.5 pl-10 text-sm text-gray-900 placeholder-gray-500 focus:outline-none focus:border-[#da234d] transition-colors w-56"
/>
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
</div>
</div>
{/* Mobile menu button */}
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="md:hidden p-2 rounded-lg bg-white/10 hover:bg-white/20 transition-colors"
data-testid="button-mobile-menu"
>
{isMobileMenuOpen ? (
<X className="w-6 h-6 text-white" />
) : (
<Menu className="w-6 h-6 text-white" />
)}
</button>
</div>
</div>
</div>
{/* Mobile menu dropdown */}
{isMobileMenuOpen && (
<div className="md:hidden border-t border-white/20 bg-bunny-dark/95 backdrop-blur-md">
<div className="px-6 py-4">
{/* Navigation Section */}
<div className="mb-6">
<h3 className="text-white text-xs font-semibold uppercase tracking-wider mb-3 opacity-70">Navigation</h3>
<nav className="flex flex-col space-y-4">
<Link
href="/"
className="text-bunny-light hover:text-white transition-colors font-medium py-1 border-l-2 border-transparent hover:border-[#da234d] pl-3"
onClick={() => setIsMobileMenuOpen(false)}
>
Startseite
</Link>
<Link
href="/folx-stadl"
className="text-bunny-light hover:text-white transition-colors font-medium py-1 border-l-2 border-transparent hover:border-[#da234d] pl-3"
onClick={() => setIsMobileMenuOpen(false)}
>
FOLX STADL
</Link>
<Link
href="/geschichte-lied"
className="text-bunny-light hover:text-white transition-colors font-medium py-1 border-l-2 border-transparent hover:border-[#da234d] pl-3"
onClick={() => setIsMobileMenuOpen(false)}
>
DIE GESCHICHTE DES LIEDES
</Link>
<Link
href="/gipfelstammtisch"
className="text-bunny-light hover:text-white transition-colors font-medium py-1 border-l-2 border-transparent hover:border-[#da234d] pl-3"
onClick={() => setIsMobileMenuOpen(false)}
>
GIPFELSTAMMTISCH
</Link>
</nav>
</div>
{/* Separator */}
<div className="border-t border-white/10 mb-4"></div>
{/* Search Section */}
<div>
<h3 className="text-white text-xs font-semibold uppercase tracking-wider mb-3 opacity-70">Suchen</h3>
<div className="relative">
<Input
type="search"
placeholder="Suchen..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="bg-white/10 border border-white/20 rounded-lg px-4 py-2.5 pl-10 text-sm text-white placeholder-white/60 focus:outline-none focus:border-[#da234d] focus:bg-white/15 transition-all w-full"
/>
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-white/60 w-4 h-4" />
</div>
</div>
</div>
</div>
)}
</div>
{/* Header Banner Ad */}
<div className="container py-4 border-b border-white/10">
<div className="flex justify-center">
<AdSenseAd
adSlot="1234567890"
adFormat="horizontal"
width={728}
height={90}
className="max-w-full"
/>
</div>
</div>
<main className="w-full pt-8 pb-8 relative">
<div className="container">
<NetflixGrid
videos={allVideos}
isLoading={isLoading}
/>
{/* Mid-content Rectangle Ad */}
<div className="flex justify-center my-8 py-4">
<AdSenseAd
adSlot="1234567891"
adFormat="rectangle"
width={336}
height={280}
/>
</div>
</div>
</main>
{/* Bottom Banner Ad */}
<div className="container py-4 border-t border-white/10">
<div className="flex justify-center">
<AdSenseAd
adSlot="1234567892"
adFormat="horizontal"
width={728}
height={90}
className="max-w-full"
/>
</div>
</div>
{/* Footer */}
<footer className="bg-bunny-dark/90 border-t border-white/10 py-8 mt-12">
<div className="container">
<div className="flex flex-col items-center justify-center space-y-4">
{/* Logo */}
<div className="flex items-center space-x-2">
<div className="w-8 h-8 bg-[#da234d] rounded-lg flex items-center justify-center shadow-lg">
<div className="w-0 h-0 border-l-[8px] border-l-white border-y-[6px] border-y-transparent ml-1 drop-shadow-sm"></div>
</div>
<h3 className="text-xl font-bold text-white">video.folx.tv</h3>
</div>
{/* Legal Links */}
<div className="flex items-center space-x-6 text-sm">
<Link href="/impressum" className="text-bunny-muted hover:text-white transition-colors">
Impressum
</Link>
<Link href="/privacy" className="text-bunny-muted hover:text-white transition-colors">
Datenschutz
</Link>
<Link href="/terms" className="text-bunny-muted hover:text-white transition-colors">
Nutzungsbedingungen
</Link>
</div>
{/* Copyright */}
<p className="text-bunny-muted text-sm text-center">
© 2025 video.folx.tv. Alle Rechte vorbehalten.
</p>
</div>
</div>
</footer>
</div>
);
}