videofolxtv/client/src/components/video-ads.tsx
sebastjanartic 72bded4b3d Introduce a new custom loading spinner component with animations
Replace existing simple loading indicators with a new animated `LoadingSpinner` component across various pages and components, including `HLSPreviewThumbnail`, `NetflixGrid`, `ThumbnailGenerator`, `VideoAds`, `VideoGrid`, `FolxStadlPage`, `GeschichteLiedPage`, `GipfelstammtischPage`, and `VideoPage`. The new component features a customizable size, text, and a pulsing gradient animation, improving the visual feedback during loading states.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 890577b1-c154-40a4-a177-a0c6d55320c3
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/890577b1-c154-40a4-a177-a0c6d55320c3/UbX8tN8
2025-09-02 10:38:08 +00:00

126 lines
4.2 KiB
TypeScript

import { useState, useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { PlayCircle, Clock, Tag, Network } from "lucide-react";
interface VideoAd {
adType: string;
adUrl?: string;
adTitle?: string;
adDuration?: number;
position?: number;
vastTag?: string;
adNetwork?: string;
priority: number;
}
interface VideoAdsProps {
videoId: string;
}
export default function VideoAds({ videoId }: VideoAdsProps) {
const { data, isLoading, error } = useQuery({
queryKey: [`/api/videos/${videoId}/ads`],
});
if (isLoading) {
return (
<Card className="p-6 border-gray-700 bg-gray-800">
<div className="text-center">
<div className="w-10 h-10 gradient-primary rounded-lg flex items-center justify-center shadow-lg animate-pulse mb-3 mx-auto">
<div className="w-0 h-0 border-l-[8px] border-l-white border-y-[6px] border-y-transparent ml-1"></div>
</div>
<div className="text-white text-sm font-medium mb-1">go4.video</div>
<div className="text-gray-400 text-xs">Loading ads...</div>
</div>
</Card>
);
}
if (error) {
return (
<Card className="p-4 border-red-200 bg-red-50">
<p className="text-red-600 text-sm">Failed to load ad metadata</p>
</Card>
);
}
const { ads = [], totalAds = 0 } = (data as any) || {};
if (totalAds === 0) {
return (
<Card className="p-4 border-gray-200 bg-gray-50">
<p className="text-gray-600 text-sm flex items-center gap-2">
<PlayCircle className="w-4 h-4" />
No ad spots configured for this video
</p>
</Card>
);
}
return (
<div className="space-y-3">
<div className="flex items-center gap-2 mb-3">
<PlayCircle className="w-5 h-5 text-blue-600" />
<h3 className="font-semibold text-gray-900">
Video Ads ({totalAds} spots)
</h3>
</div>
{ads.map((ad: VideoAd, index: number) => (
<Card key={index} className="p-4 border border-gray-200 hover:border-blue-300 transition-colors">
<div className="flex items-start justify-between gap-3">
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<Badge
variant={ad.adType === 'preroll' ? 'default' : ad.adType === 'postroll' ? 'secondary' : 'outline'}
className="text-xs"
>
{ad.adType.toUpperCase()}
</Badge>
<span className="text-sm font-medium text-gray-900">
{ad.adTitle || `${ad.adType} Advertisement`}
</span>
</div>
<div className="grid grid-cols-2 gap-3 text-sm text-gray-600">
{ad.adDuration && (
<div className="flex items-center gap-1">
<Clock className="w-3 h-3" />
<span>{ad.adDuration}s duration</span>
</div>
)}
{ad.position !== undefined && ad.adType === 'midroll' && (
<div className="flex items-center gap-1">
<PlayCircle className="w-3 h-3" />
<span>At {Math.floor(ad.position / 60)}:{(ad.position % 60).toString().padStart(2, '0')}</span>
</div>
)}
{ad.adNetwork && (
<div className="flex items-center gap-1">
<Network className="w-3 h-3" />
<span>{ad.adNetwork}</span>
</div>
)}
<div className="flex items-center gap-1">
<Tag className="w-3 h-3" />
<span>Priority {ad.priority}</span>
</div>
</div>
{ad.vastTag && (
<div className="mt-2 p-2 bg-gray-50 rounded text-xs font-mono text-gray-700 break-all">
VAST: {ad.vastTag.substring(0, 80)}...
</div>
)}
</div>
</div>
</Card>
))}
</div>
);
}