Prechádzať zdrojové kódy

Add image icon to news card

Adds a small image icon to the left of the news card when an image is present in the article description.
gpt-engineer-app[bot] 5 mesiacov pred
rodič
commit
4070bb326f
1 zmenil súbory, kde vykonal 93 pridanie a 40 odobranie
  1. 93 40
      src/components/NewsCard.tsx

+ 93 - 40
src/components/NewsCard.tsx

@@ -1,3 +1,4 @@
+
 import { NewsItem } from '@/types/news';
 import { Card, CardContent, CardHeader } from '@/components/ui/card';
 import { Badge } from '@/components/ui/badge';
@@ -5,6 +6,7 @@ import { Button } from '@/components/ui/button';
 import { Clock, Pin, ExternalLink, Eye, Trash2 } from 'lucide-react';
 import { cn } from '@/lib/utils';
 import { useAuth } from '@/hooks/useAuth';
+
 interface NewsCardProps {
   news: NewsItem;
   onTogglePin: (id: string) => void;
@@ -12,6 +14,7 @@ interface NewsCardProps {
   onDelete: (id: string) => void;
   onOpenArticle: (article: NewsItem) => void;
 }
+
 const NewsCard = ({
   news,
   onTogglePin,
@@ -19,9 +22,7 @@ const NewsCard = ({
   onDelete,
   onOpenArticle
 }: NewsCardProps) => {
-  const {
-    user
-  } = useAuth();
+  const { user } = useAuth();
 
   // Function to decode HTML entities
   const decodeHtmlEntities = (text: string) => {
@@ -29,6 +30,7 @@ const NewsCard = ({
     textarea.innerHTML = text;
     return textarea.value;
   };
+
   const getSourceColor = (category: string) => {
     switch (category) {
       case 'rss':
@@ -43,13 +45,21 @@ const NewsCard = ({
         return 'bg-muted text-muted-foreground';
     }
   };
+
   const handleCardClick = () => {
     onOpenArticle(news);
     if (!news.isRead) {
       onMarkAsRead(news.id);
     }
   };
-  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")}>
+
+  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"
+    )}>
       <CardHeader className="space-y-3">
         <div className="flex items-start justify-between gap-4">
           <div className="flex-1 space-y-2" onClick={handleCardClick}>
@@ -57,19 +67,31 @@ const NewsCard = ({
               <Badge variant="outline" className={getSourceColor(news.category)}>
                 {news.source}
               </Badge>
-              
             </div>
             
-            <h3 className={cn("font-semibold leading-tight group-hover:text-primary transition-colors", news.isRead && "text-muted-foreground")}>
+            <h3 className={cn(
+              "font-semibold leading-tight group-hover:text-primary transition-colors",
+              news.isRead && "text-muted-foreground"
+            )}>
               {decodeHtmlEntities(news.title)}
             </h3>
           </div>
           
           <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
-            <Button variant="ghost" size="sm" onClick={e => {
-            e.stopPropagation();
-            onTogglePin(news.id);
-          }} disabled={!user} className={cn("h-8 w-8 p-0", news.isPinned && "text-yellow-600", !user && "opacity-50 cursor-not-allowed")}>
+            <Button
+              variant="ghost"
+              size="sm"
+              onClick={(e) => {
+                e.stopPropagation();
+                onTogglePin(news.id);
+              }}
+              disabled={!user}
+              className={cn(
+                "h-8 w-8 p-0",
+                news.isPinned && "text-yellow-600",
+                !user && "opacity-50 cursor-not-allowed"
+              )}
+            >
               <Pin className={cn("h-4 w-4", news.isPinned && "fill-current")} />
             </Button>
           </div>
@@ -77,39 +99,70 @@ const NewsCard = ({
       </CardHeader>
       
       <CardContent className="space-y-4" onClick={handleCardClick}>
-        <p className="text-sm text-muted-foreground leading-relaxed">
-          {decodeHtmlEntities(news.description)}
-        </p>
-        
-        <div className="flex items-center justify-between">
-          <span className="text-xs text-muted-foreground">
-            {new Date(news.publishedAt).toLocaleDateString('fr-FR', {
-            day: 'numeric',
-            month: 'long',
-            hour: '2-digit',
-            minute: '2-digit'
-          })}
-          </span>
+        <div className="flex gap-3">
+          {news.imageUrl && (
+            <div className="flex-shrink-0">
+              <img
+                src={news.imageUrl}
+                alt={news.title}
+                className="w-16 h-16 object-cover rounded-md"
+              />
+            </div>
+          )}
           
-          <div className="flex items-center gap-2">
-            {!news.isRead && <Button variant="outline" size="sm" onClick={e => {
-            e.stopPropagation();
-            onMarkAsRead(news.id);
-          }} disabled={!user} className={cn("gap-1", !user && "opacity-50 cursor-not-allowed")}>
-                <Eye className="h-3 w-3" />
-                Marquer lu
-              </Button>}
+          <div className="flex-1 space-y-4">
+            <p className="text-sm text-muted-foreground leading-relaxed">
+              {decodeHtmlEntities(news.description)}
+            </p>
             
-            {news.url && <Button variant="default" size="sm" className="gap-1" onClick={e => {
-            e.stopPropagation();
-            window.open(news.url, '_blank');
-          }}>
-                <ExternalLink className="h-3 w-3" />
-                Lire
-              </Button>}
+            <div className="flex items-center justify-between">
+              <span className="text-xs text-muted-foreground">
+                {new Date(news.publishedAt).toLocaleDateString('fr-FR', {
+                  day: 'numeric',
+                  month: 'long',
+                  hour: '2-digit',
+                  minute: '2-digit'
+                })}
+              </span>
+              
+              <div className="flex items-center gap-2">
+                {!news.isRead && (
+                  <Button
+                    variant="outline"
+                    size="sm"
+                    onClick={(e) => {
+                      e.stopPropagation();
+                      onMarkAsRead(news.id);
+                    }}
+                    disabled={!user}
+                    className={cn("gap-1", !user && "opacity-50 cursor-not-allowed")}
+                  >
+                    <Eye className="h-3 w-3" />
+                    Marquer lu
+                  </Button>
+                )}
+                
+                {news.url && (
+                  <Button
+                    variant="default"
+                    size="sm"
+                    className="gap-1"
+                    onClick={(e) => {
+                      e.stopPropagation();
+                      window.open(news.url, '_blank');
+                    }}
+                  >
+                    <ExternalLink className="h-3 w-3" />
+                    Lire
+                  </Button>
+                )}
+              </div>
+            </div>
           </div>
         </div>
       </CardContent>
-    </Card>;
+    </Card>
+  );
 };
-export default NewsCard;
+
+export default NewsCard;