NewsCard.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { NewsItem } from '@/types/news';
  2. import { Card, CardContent, CardHeader } from '@/components/ui/card';
  3. import { Badge } from '@/components/ui/badge';
  4. import { Button } from '@/components/ui/button';
  5. import { Clock, Pin, ExternalLink, Eye, Trash2 } from 'lucide-react';
  6. import { cn } from '@/lib/utils';
  7. import { useAuth } from '@/hooks/useAuth';
  8. interface NewsCardProps {
  9. news: NewsItem;
  10. onTogglePin: (id: string) => void;
  11. onMarkAsRead: (id: string) => void;
  12. onDelete: (id: string) => void;
  13. onOpenArticle: (article: NewsItem) => void;
  14. }
  15. const NewsCard = ({
  16. news,
  17. onTogglePin,
  18. onMarkAsRead,
  19. onDelete,
  20. onOpenArticle
  21. }: NewsCardProps) => {
  22. const {
  23. user
  24. } = useAuth();
  25. // Function to decode HTML entities
  26. const decodeHtmlEntities = (text: string) => {
  27. const textarea = document.createElement('textarea');
  28. textarea.innerHTML = text;
  29. return textarea.value;
  30. };
  31. const getSourceColor = (category: string) => {
  32. switch (category) {
  33. case 'rss':
  34. return 'bg-blue-500/10 text-blue-700 border-blue-200';
  35. case 'youtube':
  36. return 'bg-red-500/10 text-red-700 border-red-200';
  37. case 'steam':
  38. return 'bg-gray-500/10 text-gray-700 border-gray-200';
  39. case 'actualites':
  40. return 'bg-green-500/10 text-green-700 border-green-200';
  41. default:
  42. return 'bg-muted text-muted-foreground';
  43. }
  44. };
  45. const handleCardClick = () => {
  46. onOpenArticle(news);
  47. if (!news.isRead) {
  48. onMarkAsRead(news.id);
  49. }
  50. };
  51. return <Card className={cn("group hover:shadow-lg transition-all duration-300 border-l-4 cursor-pointer", news.isPinned && "border-l-yellow-500", news.isRead && "opacity-75", !news.isRead && "border-l-primary")}>
  52. <CardHeader className="space-y-3">
  53. <div className="flex items-start justify-between gap-4">
  54. <div className="flex-1 space-y-2" onClick={handleCardClick}>
  55. <div className="flex items-center gap-2">
  56. <Badge variant="outline" className={getSourceColor(news.category)}>
  57. {news.source}
  58. </Badge>
  59. </div>
  60. <h3 className={cn("font-semibold leading-tight group-hover:text-primary transition-colors", news.isRead && "text-muted-foreground")}>
  61. {decodeHtmlEntities(news.title)}
  62. </h3>
  63. </div>
  64. <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
  65. <Button variant="ghost" size="sm" onClick={e => {
  66. e.stopPropagation();
  67. onTogglePin(news.id);
  68. }} disabled={!user} className={cn("h-8 w-8 p-0", news.isPinned && "text-yellow-600", !user && "opacity-50 cursor-not-allowed")}>
  69. <Pin className={cn("h-4 w-4", news.isPinned && "fill-current")} />
  70. </Button>
  71. </div>
  72. </div>
  73. </CardHeader>
  74. <CardContent className="space-y-4" onClick={handleCardClick}>
  75. <p className="text-sm text-muted-foreground leading-relaxed">
  76. {decodeHtmlEntities(news.description)}
  77. </p>
  78. <div className="flex items-center justify-between">
  79. <span className="text-xs text-muted-foreground">
  80. {new Date(news.publishedAt).toLocaleDateString('fr-FR', {
  81. day: 'numeric',
  82. month: 'long',
  83. hour: '2-digit',
  84. minute: '2-digit'
  85. })}
  86. </span>
  87. <div className="flex items-center gap-2">
  88. {!news.isRead && <Button variant="outline" size="sm" onClick={e => {
  89. e.stopPropagation();
  90. onMarkAsRead(news.id);
  91. }} disabled={!user} className={cn("gap-1", !user && "opacity-50 cursor-not-allowed")}>
  92. <Eye className="h-3 w-3" />
  93. Marquer lu
  94. </Button>}
  95. {news.url && <Button variant="default" size="sm" className="gap-1" onClick={e => {
  96. e.stopPropagation();
  97. window.open(news.url, '_blank');
  98. }}>
  99. <ExternalLink className="h-3 w-3" />
  100. Lire
  101. </Button>}
  102. </div>
  103. </div>
  104. </CardContent>
  105. </Card>;
  106. };
  107. export default NewsCard;