|
|
@@ -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;
|