videofolxtv/client/src/pages/FolxStadlPage.tsx
sebastjanartic 6a53907814 Update branding to consistently use video.folx.tv across the platform
Adjusts various components and pages to display "video.folx.tv" instead of "FOLX.TV", with the exception of the home page header.

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/o9BuTTJ
2025-09-04 13:47:39 +00:00

346 lines
15 KiB
TypeScript

import { useQuery } from '@tanstack/react-query';
import { Link, useLocation } from 'wouter';
import { ArrowLeft, ChevronLeft, ChevronRight, Menu, X } from 'lucide-react';
import VideoCard from '@/components/video-card';
import BunnyVideoModal from '@/components/bunny-video-modal';
import { Button } from '@/components/ui/button';
import { useState, useEffect } from 'react';
import type { Video } from '@shared/schema';
import { Input } from '@/components/ui/input';
import { Search } from 'lucide-react';
export default function FolxStadlPage() {
const [selectedVideo, setSelectedVideo] = useState<Video | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
const [, setLocation] = useLocation();
const [touchStart, setTouchStart] = useState<number>(0);
const [touchEnd, setTouchEnd] = useState<number>(0);
const itemsPerPage = 10;
const { data, isLoading } = useQuery<{videos: Video[], total: number}>({
queryKey: ['/api/videos?limit=200'],
select: (response) => response || { videos: [], total: 0 }
});
const videos = data?.videos || [];
// Filter all FOLX STADL videos (including all seasons and formats)
const folxStadlVideos = videos.filter(video =>
video.title.includes("FOLX STADL") || video.title.includes("FOLXSTADL")
);
// Pagination logic
const totalPages = Math.ceil(folxStadlVideos.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const currentVideos = folxStadlVideos.slice(startIndex, endIndex);
const handleVideoClick = (video: Video) => {
// Navigate to individual video page instead of modal
const shortId = video.id.replace(/-/g, '').substring(0, 8);
setLocation(`/video/${shortId}`);
};
const handleCloseModal = () => {
setIsModalOpen(false);
setSelectedVideo(null);
};
// Touch handlers for swipe navigation
const handleTouchStart = (e: React.TouchEvent) => {
setTouchEnd(0);
setTouchStart(e.targetTouches[0].clientX);
};
const handleTouchMove = (e: React.TouchEvent) => {
e.preventDefault();
setTouchEnd(e.targetTouches[0].clientX);
};
const handleTouchEnd = () => {
if (!touchStart || !touchEnd) return;
const distance = touchStart - touchEnd;
const isLeftSwipe = distance > 30;
const isRightSwipe = distance < -30;
if (isLeftSwipe && currentPage < totalPages) {
setCurrentPage(currentPage + 1);
}
if (isRightSwipe && currentPage > 1) {
setCurrentPage(currentPage - 1);
}
};
if (isLoading) {
return (
<div className="min-h-screen bg-bunny-dark flex items-center justify-center">
<div className="text-center">
<div className="w-16 h-16 gradient-primary rounded-lg flex items-center justify-center shadow-lg animate-pulse mb-4 mx-auto">
<div className="w-0 h-0 border-l-[12px] border-l-white border-y-[9px] border-y-transparent ml-1"></div>
</div>
<h3 className="text-white text-xl font-bold mb-2">video.folx.tv</h3>
<p className="text-bunny-light">Loading FOLX STADL...</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-bunny-dark text-white has-fixed-header">
{/* 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 gradient-primary 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-bunny-blue transition-colors group">
Startseite
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#ec4c73] to-[#e91e63] transition-all duration-300 group-hover:w-full"></span>
</Link>
<Link href="/folx-stadl" className="relative text-bunny-light hover:text-bunny-blue transition-colors group">
FOLX STADL
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#ec4c73] to-[#e91e63] transition-all duration-300 group-hover:w-full"></span>
</Link>
<Link href="/geschichte-lied" className="relative text-bunny-light hover:text-bunny-blue transition-colors group">
DIE GESCHICHTE DES LIEDES
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#ec4c73] to-[#e91e63] transition-all duration-300 group-hover:w-full"></span>
</Link>
<Link href="/gipfelstammtisch" className="relative text-bunny-light hover:text-bunny-blue transition-colors group">
GIPFELSTAMMTISCH
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-gradient-to-r from-[#ec4c73] to-[#e91e63] 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-bunny-blue 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-folx"
>
{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-bunny-blue transition-colors font-medium py-1 border-l-2 border-transparent hover:border-bunny-blue pl-3"
onClick={() => setIsMobileMenuOpen(false)}
>
Startseite
</Link>
<Link
href="/folx-stadl"
className="text-bunny-light hover:text-bunny-blue transition-colors font-medium py-1 border-l-2 border-transparent hover:border-bunny-blue pl-3"
onClick={() => setIsMobileMenuOpen(false)}
>
FOLX STADL
</Link>
<Link
href="/geschichte-lied"
className="text-bunny-light hover:text-bunny-blue transition-colors font-medium py-1 border-l-2 border-transparent hover:border-bunny-blue pl-3"
onClick={() => setIsMobileMenuOpen(false)}
>
DIE GESCHICHTE DES LIEDES
</Link>
<Link
href="/gipfelstammtisch"
className="text-bunny-light hover:text-bunny-blue transition-colors font-medium py-1 border-l-2 border-transparent hover:border-bunny-blue 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-bunny-blue 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>
{/* Main Content */}
<div
className="container pt-8 pb-8"
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
{/* Video List with Descriptions */}
<div className="space-y-6">
{currentVideos.map((video, index) => (
<div key={video.id} className="bg-black/20 backdrop-blur-sm rounded-lg p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Video Card */}
<div className="md:col-span-1">
<VideoCard
video={video}
onClick={handleVideoClick}
className="w-full hover:scale-102 transition-all duration-300 rounded-lg overflow-hidden"
hideOverlay={true}
/>
</div>
{/* Video Description */}
<div className="md:col-span-2 space-y-3">
<h3 className="text-xl font-bold text-white">{video.title}</h3>
<p className="text-bunny-light text-sm leading-relaxed">
{video.description || "Keine Beschreibung für diese Sendung verfügbar."}
</p>
<div className="flex items-center gap-4 text-xs text-bunny-muted">
<span>{Math.floor(video.duration / 60)}:{(video.duration % 60).toString().padStart(2, '0')} min</span>
<span>{video.views} Aufrufe</span>
</div>
</div>
</div>
</div>
))}
{folxStadlVideos.length === 0 && (
<div className="text-center py-16">
<p className="text-bunny-muted text-lg">Keine FOLX STADL Videos gefunden</p>
</div>
)}
</div>
{/* Bottom Pagination */}
{totalPages > 1 && (
<div className="mt-8 flex justify-center gap-1 px-4 overflow-x-auto">
{Array.from({ length: Math.min(totalPages, 5) }, (_, i) => {
let pageNum;
if (totalPages <= 5) {
pageNum = i + 1;
} else if (currentPage <= 3) {
pageNum = i + 1;
} else if (currentPage >= totalPages - 2) {
pageNum = totalPages - 4 + i;
} else {
pageNum = currentPage - 2 + i;
}
return (
<Button
key={pageNum}
variant={currentPage === pageNum ? "default" : "outline"}
size="sm"
onClick={() => setCurrentPage(pageNum)}
className={currentPage === pageNum
? "gradient-primary text-white border-none shadow-lg h-10 w-10 p-0 flex items-center justify-center"
: "border-white/20 text-white hover:bg-white/10 h-10 w-10 p-0 flex items-center justify-center"
}
>
{pageNum}
</Button>
);
})}
</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 gradient-primary 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-bunny-blue transition-colors">
Impressum
</Link>
<Link href="/privacy" className="text-bunny-muted hover:text-bunny-blue transition-colors">
Datenschutz
</Link>
<Link href="/terms" className="text-bunny-muted hover:text-bunny-blue 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>
{/* Video Modal */}
{selectedVideo && (
<BunnyVideoModal
video={selectedVideo}
isOpen={isModalOpen}
onClose={handleCloseModal}
videos={folxStadlVideos}
onVideoChange={setSelectedVideo}
/>
)}
</div>
);
}