placebo.mk/frontend/src/components/features/live-blog/PinnedLiveBlogSidebar.tsx
echo aa43f50a8c fix: localize UI text to Macedonian and fix view count display
- Change date format from 'short' to 'long' month names in Macedonian
- Translate 'views' to 'прегледи' across all components
- Translate 'shares' to 'споделувања'
- Translate 'updates' to 'ажурирања'
- Translate 'By' to 'Од' for author attribution
- Translate 'Back to articles' to 'Назад кон вести'
- Translate archive page headers to Macedonian
- Translate auto-scroll button text to Macedonian
- Translate connection status to Macedonian
- Add fallback to 0 for undefined view counts (|| 0)
2026-02-28 23:57:33 +01:00

239 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Link } from '@tanstack/react-router';
import { usePinnedLiveBlogs } from '@/queries/live-blogs';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import type { LiveBlog } from '@/lib/api';
import {
Clock,
MessageSquare,
Eye,
Pin,
Play,
Square,
ChevronRight
} from 'lucide-react';
interface PinnedLiveBlogSidebarProps {
className?: string;
maxItems?: number;
}
export function PinnedLiveBlogSidebar({
className = '',
maxItems = 3
}: PinnedLiveBlogSidebarProps) {
const { data: pinnedBlogs, isLoading, error } = usePinnedLiveBlogs();
if (isLoading) {
return (
<Card className={className}>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<Pin className="w-4 h-4" />
Во живо
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{[1, 2, 3].map((i) => (
<div key={i} className="animate-pulse space-y-2">
<div className="h-4 bg-muted rounded w-3/4"></div>
<div className="h-3 bg-muted rounded w-1/2"></div>
<div className="h-2 bg-muted rounded w-1/4"></div>
</div>
))}
</div>
</CardContent>
</Card>
);
}
if (isLoading) {
return (
<Card className={className}>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<Pin className="w-4 h-4" />
Live Coverage
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{[1, 2, 3].map((i) => (
<div key={i} className="animate-pulse space-y-2">
<div className="h-4 bg-muted rounded w-3/4"></div>
<div className="h-3 bg-muted rounded w-1/2"></div>
<div className="h-2 bg-muted rounded w-1/4"></div>
</div>
))}
</div>
</CardContent>
</Card>
);
}
if (error) {
return (
<Card className={className}>
<CardHeader>
<CardTitle className="text-lg flex items-center gap-2">
<Pin className="w-4 h-4" />
Live Coverage
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground text-center py-4">
Error loading live coverage
</p>
</CardContent>
</Card>
);
}
const displayBlogs = (pinnedBlogs || []).slice(0, maxItems);
const getStatusColor = (status: string) => {
switch (status) {
case 'live': return 'bg-green-100 text-green-800 border-green-200';
case 'ended': return 'bg-red-100 text-red-800 border-red-200';
default: return 'bg-gray-100 text-gray-800 border-gray-200';
}
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'live': return <Play className="w-3 h-3" />;
case 'ended': return <Square className="w-3 h-3" />;
default: return null;
}
};
const formatTime = (dateString: string) => {
const date = new Date(dateString);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffMins = Math.floor(diffMs / 60000);
if (diffMins < 60) {
return `${diffMins}m ago`;
} else if (diffMins < 1440) {
return `${Math.floor(diffMins / 60)}h ago`;
} else {
return `${Math.floor(diffMins / 1440)}d ago`;
}
};
const getLatestUpdate = (blog: LiveBlog) => {
if (!blog.updates || blog.updates.length === 0) return null;
return blog.updates[blog.updates.length - 1];
};
return (
<Card className={className}>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-lg flex items-center gap-2">
<Pin className="w-4 h-4" />
Live Coverage
</CardTitle>
<Badge variant="outline" className="text-xs">
{(pinnedBlogs || []).length} pinned
</Badge>
</div>
</CardHeader>
<CardContent className="pt-0">
{displayBlogs.length === 0 ? (
<div className="py-6 text-center">
<p className="text-sm text-muted-foreground">
No pinned live blogs at the moment
</p>
<p className="text-xs text-muted-foreground mt-1">
Check back later for live coverage
</p>
</div>
) : (
<>
<div className="space-y-4">
{displayBlogs.map((blog) => {
const latestUpdate = getLatestUpdate(blog);
return (
<Link
key={blog.id}
to="/live-blogs/$slug"
params={{ slug: blog.slug }}
className="block p-3 rounded-lg border hover:bg-accent/50 transition-colors group"
>
<div className="flex items-start justify-between mb-2">
<div className="flex-1">
<h4 className="font-medium text-sm mb-1 group-hover:text-primary transition-colors line-clamp-2">
{blog.title}
</h4>
{blog.description && (
<p className="text-xs text-muted-foreground line-clamp-2">
{blog.description}
</p>
)}
</div>
<Badge
className={`${getStatusColor(blog.status)} text-xs`}
variant="outline"
>
<div className="flex items-center gap-1">
{getStatusIcon(blog.status)}
{blog.status}
</div>
</Badge>
</div>
{/* Latest update preview */}
{latestUpdate && (
<div className="mt-2 p-2 bg-muted/50 rounded text-xs">
<div className="flex items-center gap-1 text-muted-foreground mb-1">
<Clock className="w-3 h-3" />
<span>{formatTime(latestUpdate.createdAt)}</span>
</div>
<p className="line-clamp-2">{latestUpdate.content}</p>
</div>
)}
{/* Stats */}
<div className="mt-2 flex items-center gap-3 text-xs text-muted-foreground">
<div className="flex items-center gap-1">
<MessageSquare className="w-3 h-3" />
<span>{blog.updates?.length || 0} ажурирања</span>
</div>
<div className="flex items-center gap-1">
<Eye className="w-3 h-3" />
<span>{blog.viewCount || 0} прегледи</span>
</div>
</div>
</Link>
);
})}
</div>
{/* View all button */}
{(pinnedBlogs || []).length > maxItems && (
<Button
variant="ghost"
size="sm"
className="w-full mt-4"
asChild
>
<Link to="/live-blogs">
View all pinned blogs
<ChevronRight className="w-3 h-3 ml-1" />
</Link>
</Button>
)}
</>
)}
</CardContent>
</Card>
);
}